Friday, November 8, 2019

Multi-threading in C# With Tasks

Multi-threading in C# With Tasks The computer programming term thread is short for  thread  of execution, in which a processor follows a specified path through your code. The concept of following more than one thread at a time introduces the subject of multi-tasking and multi-threading. An application has one or more processes in it. Think of a process as a program running on your computer. Now each process has one or more threads. A game application might have a thread to load resources from disk, another to do AI, and another to run the game as a server. In .NET/Windows,  the operating system allocates processor time to a thread. Each thread keeps track of exception handlers and the priority at which it runs, and it has somewhere to save the thread context until it runs. Thread context is the information that the thread needs to resume. Multi-Tasking With Threads Threads take up a bit of memory and creating them takes a little time, so usually, you dont want to use many. Remember, they compete for processor time. If your computer has multiple CPUs, then Windows or .NET might run each thread on a different CPU, but if several threads run on the same CPU, then only one can be active at a time and switching threads takes time. The CPU runs a thread for a few million instructions, and then it switches to another thread. All of the CPU registers, current program execution point and stack have to be saved somewhere for the first thread and then restored from somewhere else for the next thread. Creating a Thread In the namespace System.Threading, youll find the thread type. The constructor thread  (ThreadStart) creates an instance of a thread. However, in recent C# code, its more likely to pass in a lambda expression that calls the method with any parameters. If youre unsure about lambda expressions, it might be worth checking out LINQ. Here is an example of a thread that is created and started: using System; using System.Threading;namespace ex1{class Program{public static void Write1(){Console.Write(1) ;Thread.Sleep(500) ;}static void Main(string[] args){var task new Thread(Write1) ;task.Start() ;for (var i 0; i 10; i){Console.Write(0) ;Console.Write (task.IsAlive ? A : D) ;Thread.Sleep(150) ;}Console.ReadKey() ;}}} All this example does is write 1 to the console. The main thread writes a 0 to the console 10 times, each time followed by an A or D depending on whether the other thread is still Alive or Dead. The other thread only runs once and writes a 1. After the half-second delay in the Write1() thread, the thread finishes, and the Task.IsAlive in the main loop now returns D. Thread Pool and Task Parallel Library Instead of creating your own thread, unless you really need to do it, make use of a Thread Pool. From .NET 4.0, we have access to the Task Parallel Library (TPL). As  in the previous example, again we need a bit of LINQ, and yes, its all lambda expressions. Tasks uses the Thread Pool behind the scenes  but make  better use of the threads depending on the number in use. The main object in the TPL is a Task. This is a class that represents an asynchronous operation. The commonest way to start things running is with the Task.Factory.StartNew as in: Task.Factory.StartNew(() DoSomething()); Where DoSomething() is the method that is run. Its possible to create a task and not have it run immediately. In that case, just use Task like this: var t new Task(() Console.WriteLine(Hello));...t.Start(); That doesnt start the thread until the .Start() is called. In the example below, are five tasks. using System;using System.Threading;using System.Threading.Tasks;namespace ex1{class Program{public static void Write1(int i){Console.Write(i) ;Thread.Sleep(50) ;}static void Main(string[] args){for (var i 0; i 5; i){var value i;var runningTask Task.Factory.StartNew(()Write1(value)) ;}Console.ReadKey() ;}}} Run that and you  get the digits 0 through 4 output in some random order such as 03214. Thats because the order of task execution is determined by .NET. You might be wondering why the var value i is needed. Try removing it and calling Write(i), and youll see something unexpected like 55555. Why is this? Its because the task shows the value of i at the time that the task is executed, not when the task was created. By creating a new variable each time in the loop, each of the five values is correctly stored and picked up.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.