The most common examples of concurrency:
A thread is an execution path that can be executed independently of other paths eg the main thread in some program is eg calculation of an expression so it is single-threaded, and if it were multithreaded, many threads could be running in one moment eg would calculate a value in the same for a moment it could do something else in the background, some a method whose result would display eg after calculating the value.
using System;
using System.Threading;
namespace wyrlam
{
class Program
{
static void Main()
{
Thread x = new Thread(WriteY); // create a new thread
x.Start(); // execute of the method WriteY()
// at the same time, we perform operations in the main thread
Thread y = new Thread(WriteX); // create a new thread
y.Start();// execute of the method WriteX()
Thread z = new Thread(WriteZ); // create a new thread
z.Start();// execute of the method WriteZ()
for (int i = 0; i < 1000; i++) Console.Write("S");
Console.ReadKey();
}
static void WriteY()
{
for (int i = 0; i < 2000; i++) Console.Write("i");
}
static void WriteX()
{
for (int i = 0; i < 3000; i++) Console.Write("e");
}
static void WriteZ()
{
for (int i = 0; i < 4000; i++) Console.Write("m");
}
}
}
In the main thread in the “Main” method we create three other threads, methods that display characters in the console and you can see that at the same time characters from the “Main” method are displaying, i.e. our program is multithreaded.
You can also wait for the thread to finish working, then you need to add the Join() method
Thread x = new Thread(WriteY); // create a new thread
x.Start(); // execute of the method WriteY()
x.Join();
Thread y = new Thread(WriteX); // create a new thread
y.Start();// execute of the method WriteX()
y.Join();
Thread z = new Thread(WriteZ); // create a new thread
z.Start();// execute of the method WriteZ()
z.Join();
You can see the differences that the threads are executing now in turn.
We can also put the thread to sleep for a while, let’s put the thread y to sleep:
Thread y = new Thread(WriteX); // create a new thread
Thread.Sleep(3000); //suspend the thread for 3 seconds
y.Start();// execute of the method WriteX()
y.Join();
In this example, the WriteY() method will be executed immediately, then we have to wait 3 seconds to execute the next threads, but the time must be given in milliseconds.
While there is a problem with threads, look at the example below:
static void Main()
{
Thread z = new Thread(WriteZ);
z.Start();
WriteZ();
Console.ReadKey();
}
static void WriteZ()
{
if (!_done) { Console.WriteLine("Done"); _done = true; }
}
At the same time, two threads are executed that start the same method, this will display the word “Done” twice in the console.
How can you check the security of threads? The keyword “lock” is used for this. So we will secure our threads in the example below.
static bool _done;
static readonly object _locker = new object();
static void Main()
{
Thread z = new Thread(WriteZ);
z.Start();
WriteZ();
Console.ReadKey();
}
static void WriteZ()
{
lock (_locker)
{
if (!_done) { Console.WriteLine("Done"); _done = true; }
}
}
The solution used in the above example guarantees that one block can be put on one thread at a time, which means that the word “Done” will be displayed once.
Sometimes there is a need to pass data to the thread. In this case, the lambda expression is the easiest to use:
static void Main()
{
Thread z = new Thread(() => WriteZ("executing a thread z"));
z.Start();
Console.ReadKey();
}
static void WriteZ(string text)
{
Console.WriteLine(text);
}
The thread is a low-level tool designed to provide concurrency and therefore has some limitations.
Direct use of threads also worsens performance.
The Task class is the solution to all these problems.
Tasks are created in the following way:
Task.Run(() => Console.WriteLine("Hi"));
This record is the equivalent of threads in this form:
new Thread (() => Console.WriteLine ("Hi")).Start();
call Wait() method is the equivalent of the join() method in threads:
static void Main()
{
Task task = Task.Run(() =>
{
Thread.Sleep(2000);
Console.WriteLine("Hi");
});
Console.WriteLine(task.IsCompleted); // False
task.Wait(); // blocking until the task will be completed
Console.WriteLine(task.IsCompleted); // True
Console.ReadKey();
}
In the threads is difficult to do, trivial in tasks:
static void Main()
{
Task<int> task = Task.Run(() =>
{
return 3;
});
int number = task.Result;
Console.WriteLine(number);
Console.ReadKey();
}
The continuations could be translated into the sentence “after finishing doing something else”. There are two ways to define the continuation for the task, the first is important because it is used in asynchronous functions, what you will learn in the next lesson, below is an example:
static void Main()
{
Task<int> primeNumberTask = Task.Run(() =>
{
Thread.Sleep(3000);
return 34;
});
var awaiter = primeNumberTask.GetAwaiter();
awaiter.OnCompleted(() =>
{
int result = awaiter.GetResult();
Console.WriteLine(result); // record of the result
});
Console.ReadKey();
}
The second way to add a continuation is to call the ContinueWith() method:
static void Main()
{
Task<int> primeNumberTask = Task.Run(() =>
{
Thread.Sleep(3000);
return 34;
});
primeNumberTask.ContinueWith(antecedent =>
{
int result = antecedent.Result;
Console.WriteLine(result); // record 34
});
Console.ReadKey();
}
The Task.Delay() method is the asynchronous equivalent of Thread.Sleep():
static void Main()
{
Task.Delay(5000).GetAwaiter().OnCompleted(() =>
{
Console.WriteLine("Hi");
});
Console.ReadKey();
}
This content also you can find on my blog http://devman.pl/csharplan/c-language-concurrencymultithreading/
If you recognise it as useful, share it with others so that others can also use it.
Leave upvote and follow and wait for next articles :) .
We have reached the end, the knowledge in this lesson will be need in the next one, so understand the material in it well.