{"id":130,"date":"2006-06-19T00:00:00","date_gmt":"2006-06-19T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/asynchronous-processing-in-net-part-1-fundamentals\/"},"modified":"2021-04-29T15:28:29","modified_gmt":"2021-04-29T15:28:29","slug":"asynchronous-processing-in-net-part-1-fundamentals","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/asynchronous-processing-in-net-part-1-fundamentals\/","title":{"rendered":"Asynchronous processing in .NET part 1: fundamentals"},"content":{"rendered":"<h2>The basics of asynchronous processing<\/h2>\n<p>In Windows Forms applications, we often need to perform some complex processing in the background, while still continuing with other tasks, such as monitoring user input and updating the user interface. For example, if you think of a web browser, it fetches and renders a web page without the main form hanging, so we can still press the Stop or Back buttons without waiting for the current page to load. This is achieved through asynchronous processing; that is, processing that takes advantage of Windows&#8217; multi-threading abilities to perform (as it appears to the user) two tasks at the same time.<\/p>\n<p>When we call a method in the normal (synchronous) fashion, it executes on the same thread as the calling method, which will therefore block until the called method has finished executing. Consequently, if a Windows application uses only synchronous processing, it will appear very unresponsive and hang whenever any lengthy processing needs to be carried out. Users may accept this in a very simple form, where there is nothing to be done until the processing is complete, but more frequently they will be very frustrated about being unable to interact with the application, and (if the delay is too long) may even believe that the program has crashed. To call a method asynchronously, we need to spawn a new thread, perform some processing on that thread, and perhaps also be notified when this processing is complete. We also need to be able to pass data between methods executing on different threads, and ensure that this data isn&#8217;t accessed by two threads at the same time, which could result in data being compromised or in non-repeatable reads.<\/p>\n<p>In this article we&#8217;ll see how to take advantage of asynchronous processing in .NET code. The code in this article is valid both for .NET 1.1 and .NET 2.0; we&#8217;ll look in a second article at ways to synchronize the processing occurring on different threads and the problems that can arise if this isn&#8217;t done correctly; and finally in a third article we&#8217;ll look at the new asynchronous features provided by .NET 2.0.<\/p>\n<h3>Starting a new thread <\/h3>\n<p>When we want to perform an asynchronous operation, the first task is to start execution of a method on a new thread. .NET provides a number of ways to <i>spawn<\/i> a new thread within the process of our application. However, the fundamental choice to make is whether we want the new thread to run on a thread that we manage entirely from our code, or on a thread taken from the thread pool, which is managed by the system.<\/p>\n<h4>Using the thread class <\/h4>\n<p>We&#8217;ll look at the thread pool shortly, but let&#8217;s look first at starting a new thread that is controlled entirely by your application. Probably the simplest way to spawn a new thread in .NET is to call the <code>Start()<\/code> method of an instance of the <code>System.Threading.Thread<\/code> class that is initialized with a <code>ThreadStart<\/code> delegate instance pointing to the method that we want to run on the new thread. For example:<\/p>\n<pre>\r\n\/\/ Instantiate a ThreadStart delegate pointing to the method\r\n\/\/ to be run asynchronously\r\n\/\/ (in this case, our asynchronous method is called DoStuff)\r\nThreadStart ts = new ThreadStart(DoStuff);\r\n\r\n\/\/ Initialize a new Thread object with this delegate\r\nThread t = new Thread(ts);\r\n\r\n\/\/ Start the method\r\nt.Start();<\/pre>\n<p>One of the problems with this approach is that the <code>ThreadStart<\/code> delegate is void and parameterless, so this technique can&#8217;t be used to run methods that have a return value or parameters. If you&#8217;re writing a method to be run asynchronously in this way, you&#8217;ll need to design it to match this delegate. If you want to call an existing method in this way, you&#8217;ll need to create a new method that is void and parameterless, pass the required data into it (we&#8217;ll see how to do this shortly), and then call the existing method from there.<\/p>\n<h4>The thread pool <\/h4>\n<p>A .NET application has available to it a pool of threads that are managed by the system. When we queue a task to be executed on a thread-pool thread, it will be executed on any free thread taken from the pool. The thread pool is created the first time it is used by an application, and contains by default a maximum of 25 worker threads per processor (this can be changed by calling the unmanaged method <code>CorSetMaxThreads<\/code>, defined in <code>mscoree.h<\/code>). If the maximum number of threads is reached and all threads in the pool are busy when the method is called, a thread will be allocated when one becomes available.<\/p>\n<p>You can establish the maximum number of thread-pool threads by calling the <code>ThreadPool.GetMaxThreads()<\/code> static method, and you can determine how many threads are currently available by calling the <code>ThreadPool.GetAvailableThreads()<\/code> static method. Both these methods take two output parameters of type int. The first will be populated with the number of worker threads available for background processing and the second the number of additional threads known as completion port threads that can be started for asynchronous I\/O operations.<\/p>\n<p>The thread pool is best suited for applications that require multiple threads that will execute only for a short time or spend most of their time in the sleeping state, as threads that take a long time will block other tasks. The thread pool allows the system to optimise thread usage for this scenario. Also, because the thread pool is managed by the system, it allows the system to optimise thread usage even with respect to other processes, about which your application knows nothing. Moreover, the thread will quite likely already exist, so there&#8217;s no start up overhead.<\/p>\n<p>Method calls can be queued to the thread pool in a number of ways: using asynchronous delegates, by calling the <code>ThreadPool.QueueUserWorkItem()<\/code> method, or by calling a <code>BeginXxxx()<\/code> method on a built-in class, such as <code>HttpWebRequest.BeginGetResponse()<\/code>. Here we&#8217;ll focus on asynchronous delegates.<\/p>\n<h4>Asynchronous delegates <\/h4>\n<p>Probably the easiest way to invoke a method on a thread-pool thread is to call an asynchronous delegate. When our source code is compiled, the compiler generates a class for each delegate type that contains methods called <code>BeginInvoke()<\/code> and <code>EndInvoke()<\/code>. The implementation for these methods is provided by the CLR, and we can call them on any delegate instance. <code>BeginInvoke()<\/code> causes the method represented by delegate to be queued to the thread pool, while <code>EndInvoke()<\/code> blocks the thread on which it is called until the asynchronous method has completed, and allows us to retrieve the return value for that method. The parameters for <code>BeginInvoke()<\/code> are:<\/p>\n<ul>\n<li>Any parameters required for the asynchronous method  <\/li>\n<li>An <code>AsyncCallback<\/code> delegate instance that points to the method that will be called when the invoked method terminates  <\/li>\n<li>An object to contain state for the asynchronous operation <\/li>\n<\/ul>\n<p><code>BeginInvoke()<\/code> returns an object that implements the <code>IAsyncResult<\/code> interface, which can be used to access the asynchronous delegate instance and hence call <code>EndInvoke()<\/code> to get the return value from the asynchronous method. This <code>IAsyncResult<\/code> implementation is passed into the callback method, so <code>EndInvoke()<\/code> can be called either from the original method, or from the callback method. Note that the <code>callback<\/code> method executes on the thread-pool thread, not the original thread, so in the latter scenario you may need to pass the return value back to the original thread.<\/p>\n<p>The following very simple console application demonstrates the user of asynchronous delegates. Here we use the technique to execute on a thread-pool thread a static method named <code>Add()<\/code> that returns (as a <code>long<\/code>) the sum of two integers passed in as parameters (after a brief delay to simulate the type of complicated operation that you&#8217;d normally want to perform asynchronously). When the method has finished executing, a callback method named <code>PrintResult()<\/code> will be invoked. Like all callback methods for use with asynchronous delegates, this has a return type of <code>void<\/code> and takes a single parameter of type <code>IAsyncResult<\/code>.<\/p>\n<pre>\r\nusing System;\r\nusing System.Threading;\r\nusing System.Runtime.Remoting.Messaging;\r\n\r\nnamespace AsyncTest\r\n{\r\n   public delegate long AddDelegate(int x, int y);\r\n\r\n   class Class1\r\n   {\r\n      [STAThread]\r\n      static void Main(string[] args)\r\n      {\r\n         int x = 23;\r\n         int y = 42;\r\n         AddDelegate addDelegate = new AddDelegate(Add);\r\n         AsyncCallback ac = new AsyncCallback(PrintResult);\r\n         IAsyncResult ar = addDelegate.BeginInvoke(x, y, ac, null);\r\n         Console.ReadLine();\r\n      }\r\n\r\n      static long Add(int x, int y)\r\n      {\r\n         Thread.Sleep(1000);\r\n         return x + y;\r\n      }\r\n\r\n      static void PrintResult(IAsyncResult ar)\r\n\r\n      {\r\n         AddDelegate addDelegate = (AddDelegate)\r\n((AsyncResult)ar).AsyncDelegate;\r\n         long sum = addDelegate.EndInvoke(ar);\r\n         Console.WriteLine(sum);\r\n      }\r\n   }\r\n}<\/pre>\n<p>The <code>PrintResult()<\/code> method takes the <code>IAsyncResult<\/code> implementation that the system passes into it when the method is invoked, and casts it to <code>System.Runtime.Remoting.Messaging.AsyncResult<\/code>, which is the actual type of the object. This class has a property named <code>AsyncDelegate<\/code>, which we call to retrieve the delegate instance on which the <code>BeginInvoke()<\/code> call was made. This is returned as an object, so we need to cast it to our delegate type. We can then call its <code>EndInvoke()<\/code> method, passing in the <code>IAsyncResult<\/code> implementation, and this returns the value returned from our asynchronous method call &#8211; the sum of the two numbers we wanted to add. All that remains to do is print the result out.<\/p>\n<h3>The Thread class <\/h3>\n<p>Once we&#8217;ve started a new thread, we can manipulate it using the <code>System.Threading.Thread<\/code> class. We can get a reference to an instance of this class representing a specific thread either by starting a thread manually (see the section on &#8216;Starting a New Thread&#8217; above) or by calling the <code>Thread.CurrentThread<\/code> static property, which returns a <code>Thread<\/code> instance representing the thread on which the call is executed.<\/p>\n<p>The Thread class provides a number of methods and properties that provide information about the thread and are very useful for asynchronous programming. We won&#8217;t look at all of these here, as the details can be seen on MSDN, and we&#8217;ll delay looking at some of the functionality until Part 2. We&#8217;ve already met the static <code>Sleep()<\/code> method, which blocks the current thread for a set number of milliseconds or for a <code>TimeSpan<\/code> instance. If we specify a value of zero, this will give other threads a chance to execute without actually blocking this one for a set time.<\/p>\n<p>We can also suspend a thread indefinitely by calling the <code>Suspend()<\/code> method, and restart it by calling <code>Resume()<\/code>. If a thread is already suspended when <code>Suspend()<\/code> is called, the call is ignored, but if it isn&#8217;t in the suspended state when <code>Resume()<\/code> is called, a <code>ThreadStateException<\/code> will be thrown.<\/p>\n<h3>The ThreadState property <\/h3>\n<p>For this reason, we need to be able to examine the current state of a thread, and this is available through the <code>ThreadState<\/code> property. This returns a bitmasked <code>ThreadState<\/code> enumeration value, consisting of a combination of the following values:<\/p>\n<table>\n<thead>\n<tr>\n<td valign=\"top\">\n<p><b>Name<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Description<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Value<\/b><\/p>\n<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><code>Running<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p>The thread is running.<\/p>\n<\/td>\n<td valign=\"top\">\n<p>0<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>StopRequested<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p>The thread is being requested to stop (internal use only).<\/p>\n<\/td>\n<td valign=\"top\">\n<p>1<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>SuspendRequested<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p>A request has been made to suspend the thread.<\/p>\n<\/td>\n<td valign=\"top\">\n<p>2<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Background<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p>The thread is a background thread.<\/p>\n<\/td>\n<td valign=\"top\">\n<p>4<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Unstarted<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p>The <code>Thread.Start()<\/code> method has not yet been called on the thread.<\/p>\n<\/td>\n<td valign=\"top\">\n<p>8<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Stopped<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p>The thread has stopped.<\/p>\n<\/td>\n<td valign=\"top\">\n<p>16<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>WaitSleepJoin<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p>The thread is blocked because one of the <code>Wait()<\/code>, <code>Sleep()<\/code> or <code>Join()<\/code> methods has been called.<\/p>\n<\/td>\n<td valign=\"top\">\n<p>32<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Suspended<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p>The thread has been suspended.<\/p>\n<\/td>\n<td valign=\"top\">\n<p>64<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>AbortRequested<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p>A <code>ThreadAbortException<\/code> is pending on the thread.<\/p>\n<\/td>\n<td valign=\"top\">\n<p>128<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Aborted<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p>A <code>ThreadAbortException<\/code> has been thrown on the thread and the thread is in the <code>Stopped<\/code> state.<\/p>\n<\/td>\n<td valign=\"top\">\n<p>256<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Because this returned value is a bitmask, we need to perform a bitwase <code>AND<\/code> operation on the value to determine whether it includes a specific value. For example, to see if the current thread is a background thread, we would use the following code:<\/p>\n<pre>\r\n\/\/ Get a reference to the current thread\r\nThread t = Thread.CurrentThread;\r\n\r\n\/\/ Check whether the ThreadState includes the Background value\r\nif ((t.ThreadState &amp; ThreadState.Background) == ThreadState.Background)\r\n{\r\n   \/\/ Thread is a background thread\r\n}<\/pre>\n<h3>Aborting threads <\/h3>\n<p>To abort a thread, we can call the <code>Abort()<\/code> method on the thread we wish to terminate. This causes the CLR to throw a <code>ThreadAbortException<\/code> on that thread. This exception can of course be caught and handled just like any other .NET exception object. We can even stop the abort by calling the <code>Thread.ResetAbort()<\/code> static method. Note that the exception must be handled on the same thread that it&#8217;s thrown on &#8211; it won&#8217;t be bubbled up to the thread from which the asynchronous method call was made. You therefore need to be very careful when aborting threads from a separate thread: you need to make sure that aborting the thread won&#8217;t cause data to be lost or corrupted, so it&#8217;s a good idea to catch <code>ThreadAbortException<\/code>s in any method that may be executing if a thread is to be aborted in this way.<\/p>\n<h3>Passing data between threads <\/h3>\n<p>We saw earlier that when we create a new <code>Thread<\/code> object, the delegate we pass into the constructor must be parameterless and <code>void<\/code>. This means that any data passed between the threads must be stored in either static fields or fields of a class instance. The following example illustrates this (and also potentially indicates the sort of bugs that can occur in multi-threaded applications). The class for the example has a static integer field called <code>count<\/code>. This is used in a static method named <code>Start()<\/code>, which reads and increments this static value, and displays it together with the name of the thread on which the method is executing, before sleeping to allow other threads to execute. This entire procedure is then repeated four times. The <code>Main()<\/code> method for the example calls this method asynchronously five times, each time giving the thread a different name based:<\/p>\n<pre>\r\nusing System;\r\nusing System.Threading;\r\n\r\nnamespace AsyncTest2\r\n{\r\n   class Class1\r\n   {\r\n      public static int count = 0;\r\n\r\n      [STAThread]\r\n      static void Main(string[] args)\r\n      {\r\n         for (int i = 1; i &lt; 6; i++)\r\n         {\r\n            ThreadStart tsDelegate = new ThreadStart(Start);\r\n            Thread t = new Thread(tsDelegate);\r\n            t.Name = \u201cThread \u201d + i;\r\n            t.Start();\r\n         }\r\n      }\r\n\r\n      public static void Start()\r\n      {\r\n         for (int i = 0; i &lt; 5; i++)\r\n         {\r\n            Class1.count++;\r\n            Console.WriteLine(\u201c{0} ({1})\u201d, count, Thread.CurrentThread.Name);\r\n            Thread.Sleep(0);\r\n         }\r\n      }\r\n   }\r\n}<\/pre>\n<pre>\r\n1 (Thread 1)\r\n2 (Thread 2)\r\n3 (Thread 3)\r\n4 (Thread 4)\r\n5 (Thread 5)\r\n6 (Thread 1)\r\n\r\n\u2026\r\n25 (Thread 5)<\/pre>\n<h3>Synchronization and the lock keyword <\/h3>\n<p>The problem with this code is that we don&#8217;t know when the operating system will switch execution from one thread to another, so it&#8217;s perfectly feasible that between reading the value of count and incrementing it, another thread will have read and incremented the value, so the value of count will in effect only be incremented once instead of twice. To see this happen, change the <code>Start()<\/code> method so that the call to <code>Thread.Sleep()<\/code> occurs between reading and incrementing the value of count:<\/p>\n<pre>\r\n public static void Start() {\r\n \tfor (int i = 0; i &lt; 5; i++) {\r\n \t\tint temp = Class1.count;\r\n \t\tThread.Sleep(0);\r\n \t\tClass1.count = temp + 1;\r\n \t\tConsole.WriteLine(\"{0} ({1})\", count, Thread.CurrentThread.Name);\r\n \t}\r\n }<\/pre>\n<pre>1 (Thread 5)\r\n1 (Thread 4)\r\n1 (Thread 3)\r\n1 (Thread 2)\r\n1 (Thread 1)\r\n2 (Thread 5)\r\netc...<\/pre>\n<p>Although we&#8217;ve deliberately made the bug obvious and easy to replicate in this case, this type of error can frequently lead to very subtle bugs (called <b>race conditions<\/b>) that are extremely difficult to reproduce and track down. For this reason, it&#8217;s important to ensure that multiple threads don&#8217;t access the same piece of data at the same time, particularly when changing it. This process of controlling how multiple threads interact with each other is known as synchronization. We&#8217;ll look at .NET&#8217;s synchronization features in Part 2, but for the moment we&#8217;ll introduce the most common synchronization device &#8211; the C# <code>lock<\/code> keyword ( <code>SyncLock<\/code> in VB).<\/p>\n<p>The lock keyword places a lock known as a monitor on a specific section of code (called a <b>critical section<\/b>), using a particular object. Only one thread can have a monitor lock on any particular object at the same time, so if a second thread requests a lock on the same object, it will be blocked until the first thread exits the protected code section and releases the lock. The compiled code produced by the <code>lock<\/code> keyword releases the lock within a <code>finally<\/code> section, so this ensures that the lock is always released, even if an exception occurs in the protected code.<\/p>\n<p>The object used for the lock must be an instance of a reference type. The most common object is the current instance of the class that the code is running in, by using the <code>this<\/code> keyword, or (if the code is in a static method) the <code>Type<\/code> object for the class. For example:<\/p>\n<pre>lock (this){&#160;&#160; \/\/ Code here is protected, and no other thread can execute code&#160;&#160; \/\/ that's locked with this object}<\/pre>\n<pre>lock (typeof(Class1)){&#160;&#160; \/\/ No other thread can execute code locked with the Class1 Type object}<\/pre>\n<pre>\r\n      public static void Start()\r\n      {\r\n         for (int i = 0; i &lt; 5; i++)\r\n         {\r\n            lock (typeof(Class1))\r\n            {\r\n               int temp = Class1.count;\r\n               Thread.Sleep(0);\r\n               Class1.count = temp + 1;\r\n            }\r\n            Console.WriteLine(\u201c{0} ({1})\u201d, count, Thread.CurrentThread.Name);\r\n         }\r\n      }<\/pre>\n<pre>\r\n1 (Thread 1)\r\n2 (Thread 2)\r\n3 (Thread 2)\r\netc\u2026<\/pre>\n<h2>A multi-threaded Windows Forms application <\/h2>\n<p>To illustrate working with threads in .NET, we&#8217;ve provided a simple application that demonstrates some of the functionality available. The application displays a number of coloured strings scrolling across a panel at different angles and different speeds. The movement of each string is controlled on a separate, manually-started thread. The user can change the speed and angle of existing strings, and can also start new threads, and abort, pause or resume existing threads. The interface appears as in the screenshot below:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/248-multithreading.jpg\" alt=\"248-multithreading.jpg\" \/><\/p>\n<p>Each string is represented by an instance of a class called <code>DrawString<\/code>. This class contains information about the colour of the string, its current position, speed and angle, and whether or not it&#8217;s the selected string that the user can manipulate. It will also contain references to the GDI+ Brush object used to draw the string, the parent Windows control on which it will be painted, and the thread on which it&#8217;s executing:<\/p>\n<pre>\r\npublic class DrawString : IDisposable\r\n{\r\n   private string text;\r\n   private float angle;\r\n   private float x;\r\n   private float y;\r\n   private bool selected;\r\n   private Color colour;\r\n   private int speed;\r\n   private SolidBrush brush;\r\n   private Control ctrl;\r\n   private Thread thread;<\/pre>\n<p>When a <code>DrawString<\/code> object is instantiated, we store the initial details of its appearance and position in the class&#8217;s fields, together with the reference to the parent Windows control. Whenever a new string is created, it will automatically be set as the selected string, so we set the <code>selected<\/code> field to <code>true<\/code>. We also create a GDI+ <code>SolidBrush<\/code> object of the correct colour, which will be used when we draw the control:<\/p>\n<pre>\r\n public DrawString(string text, Color colour, float angle, float x, float y, int speed, Control ctrl) {\r\n \tthis.text = text;\r\n \tthis.angle = angle;\r\n \tthis.x = x;\r\n \tthis.y = y;\r\n \tthis.colour = colour;\r\n \tthis.speed = speed;\r\n \tthis.ctrl = ctrl;\r\n \tselected = true;\r\n \tbrush = new SolidBrush(colour);\r\n }<\/pre>\n<p>In order to access these fields from the Windows Form, we&#8217;ll create public properties. The angle and speed can be changed by the user, so these are read-write properties. We&#8217;ll also use set the <code>Selected<\/code> property to read-write, as this will need to be changed when a new string is selected. The other properties are all read-only:<\/p>\n<pre>\r\n public string Text {\r\n \tget {\r\n \t\treturn text;\r\n \t}\r\n }\r\n public float Angle {\r\n \tget {\r\n \t\treturn angle;\r\n \t}\r\n \tset {\r\n \t\tangle = value;\r\n \t}\r\n }\r\n public float X {\r\n \tget {\r\n \t\treturn x;\r\n \t}\r\n }\r\n public float Y {\r\n \tget {\r\n \t\treturn y;\r\n \t}\r\n }\r\n public Color Colour {\r\n \tget {\r\n \t\treturn colour;\r\n \t}\r\n }\r\n public SolidBrush Brush {\r\n \tget {\r\n \t\treturn brush;\r\n \t}\r\n }\r\n public int Speed {\r\n \tget {\r\n \t\treturn speed;\r\n \t}\r\n \tset {\r\n \t\tspeed = value;\r\n \t}\r\n }\r\n public bool Selected {\r\n \tget {\r\n \t\treturn selected;\r\n \t}\r\n \tset {\r\n \t\tselected = value;\r\n \t}\r\n }\r\n public Thread Thread {\r\n \tget {\r\n \t\treturn thread;\r\n \t}\r\n }<\/pre>\n<p>The movement of the string across the panel is controlled by a method named <code>Start()<\/code>. This is the method that is called on a new thread, and so we start by storing a reference to the <code>Thread<\/code> object for the current thread. This will be used to pause, restart and abort the thread from the main application thread. To prevent a <code>ThreadAbortException<\/code> bubbling up to the main thread if the thread is aborted, we wrap the code for this method in a <code>try<\/code> block and catch any <code>ThreadAbortExceptions<\/code>.<\/p>\n<p>The bulk of the method consists of an infinite loop; on every iteration of this loop, we call <code>Thread.Sleep()<\/code> to pause the thread for a time depending on the speed of the string, and then increase the X and Y coordinates of the string according to the angle in which it&#8217;s moving. When one edge of the panel is reached, the X or Y coordinate is set to the opposite edge. Note that we lock the thread before altering the X and Y values. This is because the X and Y values are read from the main thread when the panel is drawn, so we need to ensure that the values aren&#8217;t read while they&#8217;re in the process of being rewritten. Once the values have been updated, we refresh the control so that the string will appear in its new position.<\/p>\n<pre>\r\n public void Start() {\r\n \ttry {\r\n \t\tthread = Thread.CurrentThread;\r\n \t\tfor (;;) {\r\n \t\t\tThread.Sleep(new TimeSpan((11 - speed) * 100000));\r\n \t\t\tlock(this) {\r\n \t\t\t\tx += (float) Math.Cos(Math.PI * angle \/ 180);\r\n \t\t\t\ty += (float) Math.Sin(Math.PI * angle \/ 180);\r\n \t\t\t\tif (x &gt;= ctrl.Width) x = x - ctrl.Width;\r\n \t\t\t\tif (y &gt;= ctrl.Height) y = y - ctrl.Height;\r\n \t\t\t\tif (x &lt; 0) x = x + ctrl.Width;\r\n \t\t\t\tif (y &lt; 0) y = y + ctrl.Height;\r\n \t\t\t}\r\n \t\t\tctrl.Refresh();\r\n \t\t}\r\n \t} catch (ThreadAbortException) {}\r\n }<\/pre>\n<p>The last method of the <code>DrawString<\/code> class is the <code>Dispose()<\/code> method. Here we simply dispose of the <code>Brush<\/code> object:<\/p>\n<pre> public void Dispose() {\r\n \tbrush.Dispose();\r\n }\r\n }\r\n <\/pre>\n<p>We can now turn to the <code>MultithreadExample<\/code> class that represents our Windows form. We won&#8217;t go through all the UI code for this class, as much of it is autogenerated by VS, but instead highlight the relevant added code.<\/p>\n<p>We add four private fields to the form class: an <code>ArrayList<\/code> that contains all the <code>DrawString<\/code> instances; a reference to the currently selected <code>DrawString<\/code>; and two <code>Font<\/code> objects. The first of these is used to draw the selected string, and the other is used for all other strings:<\/p>\n<pre>\r\nprivate ArrayList drawStrings;\r\nprivate DrawString selectedString;\r\nprivate Font boldFont = new Font(\"Arial\", 11, FontStyle.Bold);\r\nprivate Font normalFont = new Font(\"Arial\", 10);<\/pre>\n<p>In the constructor for the form, we have the usual call to <code>InitializeComponent()<\/code>, and then create the <code>drawStrings<\/code> <code>ArrayList<\/code>. Next, we start off a string to get things going. First we calculate a random colour, speed, angle and position for the string, and pass these into the <code>DrawString<\/code> constructor. We then add the new <code>DrawString<\/code> object to the <code>drawStrings<\/code> array and set it as the selected string. Finally, we need to execute the <code>DrawString<\/code> object&#8217;s <code>Start()<\/code> method on a new thread. To do this, we create a new <code>ThreadStart<\/code> delegate instance that points to this method, and use that to instantiate a new Thread object. We want the application to terminate once the form is closed and the main thread has finished, so all the <code>DrawString<\/code> threads must be background threads. Therefore, we set the Thread&#8217;s <code>IsBackground<\/code> property to <code>true<\/code> before we call its <code>Start()<\/code> method to start the execution and set the string moving.<\/p>\n<pre>\r\npublic MultithreadExample() {\r\n\tInitializeComponent();\r\n\tdrawStrings = new ArrayList();\r\n\tRandom rnd = new Random();\r\n\tint speed = rnd.Next(10) + 1;\r\n\tint x = rnd.Next(panel1.Width);\r\n\tint y = rnd.Next(panel1.Height);\r\n\tint angle = rnd.Next(360);\r\n\tint red = rnd.Next(256);\r\n\tint green = rnd.Next(256);\r\n\tint blue = rnd.Next(256);\r\n\tDrawString ds = new DrawString(\"First string\", Color.FromArgb(red, green, blue), angle, x, y, speed, panel1);\r\n\tdrawStrings.Add(ds);\r\n\tselectedString = ds;\r\n\tThreadStart startDelegate = new ThreadStart(ds.Start);\r\n\tThread t = new Thread(startDelegate);\r\n\tt.IsBackground = true;\r\n\tt.Start();\r\n}<\/pre>\n<p>The code to draw the strings is placed in the <code>panel1_Paint()<\/code> method, which is called whenever the panel control is redrawn. This has little to do with asynchronous processing, except that we lock each <code>DrawString<\/code> object before reading its properties to ensure data integrity, so we&#8217;ll just glance over it quickly. We get the GDI+ <code>Graphics<\/code> object from the <code>PaintEventArgs<\/code> object that is passed into the method. For each <code>DrawString<\/code> object in the <code>drawStrings<\/code> array, we transform the (0, 0) coordinates of the control to the coordinates of the <code>DrawString<\/code> object, rotate the drawing direction to the required angle, and then draw the string using the information in the <code>DrawString<\/code> object. Finally, we reset the coordinates and rotation of the control, so that the next string won&#8217;t be positioned relative to this one:<\/p>\n<pre>\r\nprivate void panel1_Paint(object sender,\r\nSystem.Windows.Forms.PaintEventArgs e)\r\n{\r\n   Graphics dc = e.Graphics;\r\n   StringFormat format = new StringFormat(StringFormat.GenericTypographic);\r\n   foreach (DrawString ds in drawStrings)\r\n   {\r\n      lock (ds)\r\n      {\r\n         dc.TranslateTransform(ds.X, ds.Y);\r\n         dc.RotateTransform(ds.Angle);\r\n         dc.DrawString(ds.Text, ds.Selected ? boldFont : normalFont,\r\nds.Brush, 0, 0, format);\r\n         dc.ResetTransform();\r\n      }\r\n   }\r\n}<\/pre>\n<p>One important point to note is that this method is always called by the CLR on the main application thread for the Windows form. The main thread for a Windows Forms application serves as the user interface thread, and is used to handle user input and to update the UI. Updating the UI from other threads is not allowed. All updates to the UI using GDI+ should be made from the method for the control&#8217;s <code>Paint<\/code> event, or its overwritten <code>OnPaint()<\/code> method. These methods will be called on the UI thread as necessary by the CLR.<\/p>\n<p>When a new string is created by the user, we instantiate a new <code>DrawString<\/code> object based on the information supplied by the user. The code to start the new string is identical to that which we saw when we created the first string when the application started:<\/p>\n<pre>\r\n private void button1_Click(object sender, System.EventArgs e) {\r\n\tif (textBox1.Text.Trim() == \"\") return;\r\n\tif (selectedString != null) selectedString.Selected = false;\r\n\tDrawString ds = new DrawString(textBox1.Text, label7.BackColor, (float) numericUpDown1.Value, (float) numericUpDown2.Value, (float) numericUpDown3.Value, (int) numericUpDown4.Value, panel1);\r\n\tselectedString = ds;\r\n\tdrawStrings.Add(ds);\r\n\tpanel1.Refresh();\r\n\tThreadStart startDelegate = new ThreadStart(ds.Start);\r\n\tThread t = new Thread(startDelegate);\r\n\tt.IsBackground = true;\r\n\tt.Start();\r\n}<\/pre>\n<pre>\r\nprivate void MultithreadExample_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e) {\r\n\t\tif (selectedString == null) return;\r\n\t\tKeys keyPress = e.KeyCode;\r\n\t\tswitch (keyPress) {<\/pre>\n<p>If the F1 or F2 key was pressed, we will increase or decrease the speed of the string&#8217;s movement by modifying its <code>Speed<\/code> property (up to a maximum of 10, from a minimum of 1). We acquire a lock on the selected <code>DrawString<\/code> object to ensure the data integrity of the speed:<\/p>\n<pre>\r\n case (Keys.F1):\r\n \t{\r\n \t\tlock(selectedString) {\r\n \t\t\tif (selectedString.Speed &lt; 10) selectedString.Speed++;\r\n \t\t}\r\n \t\tbreak;\r\n \t}\r\n case (Keys.F2):\r\n \t{\r\n \t\tlock(selectedString) {\r\n \t\t\tif (selectedString.Speed &gt; 1) selectedString.Speed--;\r\n \t\t}\r\n \t\tbreak;\r\n \t}<\/pre>\n<p>If F3 or F4 is pressed, we increase or decrease the angle of the string by 10 degrees, and then refresh the display. Again, we acquire a lock on the selected <code>DrawString<\/code> object:<\/p>\n<pre>\r\n case (Keys.F3):\r\n \t{\r\n \t\tlock(selectedString) {\r\n \t\t\tselectedString.Angle -= 10;\r\n \t\t\tif (selectedString.Angle &lt; 0) selectedString.Angle += 360;\r\n \t\t}\r\n \t\tpanel1.Refresh();\r\n \t\tbreak;\r\n \t}\r\n case (Keys.F4):\r\n \t{\r\n \t\tlock(selectedString) {\r\n \t\t\tselectedString.Angle += 10;\r\n \t\t\tif (selectedString.Angle &gt;= 360) selectedString.Angle -= 360;\r\n \t\t}\r\n \t\tpanel1.Refresh();\r\n \t\tbreak;\r\n \t}<\/pre>\n<p>When the user presses F5, we deselect the current <code>DrawString<\/code> object, and move the selection to the next item in the <code>drawStrings<\/code> array. We get the index of the currently selected item in the array, and increment it; if the selected item was the last, we move back to the beginning (position 0). Then we set the <code>Selected<\/code> property of the originally selected <code>DrawString<\/code> object to <code>false<\/code>, set the <code>selectedString<\/code> field to refer to the new string, and set its <code>Selected<\/code> property to <code>true<\/code>. Finally we refresh the display so that the user will be able to see the new string has been selected:<\/p>\n<pre>\r\n case (Keys.F5):\r\n \t{\r\n \t\tint index = drawStrings.IndexOf(selectedString);\r\n \t\tif (++index &gt; drawStrings.Count - 1) index = 0;lock(selectedString) {\r\n \t\t\tselectedString.Selected = false;\r\n \t\t}\r\n \t\tselectedString = (DrawString) drawStrings[index];lock(selectedString) {\r\n \t\t\tselectedString.Selected = true;\r\n \t\t}\r\n \t\tpanel1.Refresh();\r\n \t\tbreak;\r\n \t}<\/pre>\n<p>If the user presses F6, we need to abort the thread for the selected string, remove the <code>DrawString<\/code> object from the <code>drawStrings<\/code> array, and move the selection to the next string. Before aborting the thread, we need to check first that it&#8217;s not in the suspended state, as trying to abort a suspended thread generates an exception. If it is, we resume the thread. Then we abort the thread by calling the <code>Abort()<\/code> method of the <code>Thread<\/code> object exposed through the <code>DrawString<\/code>&#8216;s <code>Thread<\/code> property. We don&#8217;t need to catch the <code>ThreadAbortException<\/code> here, as that was caught in the <code>DrawString<\/code>&#8216;s <code>Start() <\/code>method. After we&#8217;ve removed the string from the <code>drawStrings<\/code> array, the index value will point to the next item in the array unless it was the last item (in which case we need to set the index back to zero). If there are no more strings left in the array, we set the selectedString field to null; otherwise, we set it to the next <code>DrawString<\/code>, and set its <code>Selected<\/code> property to <code>true<\/code>. Finally, we update the display so that the deleted string will be removed from the panel:<\/p>\n<pre>\r\n case (Keys.F6):\r\n \t{\r\n \t\tint index = drawStrings.IndexOf(selectedString);drawStrings.Remove(selectedString);\r\n \t\tif ((selectedString.Thread.ThreadState &amp; ThreadState.Suspended) == ThreadState.Suspended) selectedString.Thread.Resume();selectedString.Thread.Abort();selectedString.Dispose();\r\n \t\tif (index &gt; drawStrings.Count - 1) index = 0;\r\n \t\tif (drawStrings.Count == 0) selectedString = null;\r\n \t\telse {\r\n \t\t\tselectedString = (DrawString) drawStrings[index];\r\n \t\t\tlock(selectedString) {\r\n \t\t\t\tselectedString.Selected = true;\r\n \t\t\t}\r\n \t\t}\r\n \t\tpanel1.Refresh();\r\n \t\tbreak;\r\n \t}<\/pre>\n<p>The last two key presses we want to handle are F7 and F8, which are used respectively to suspend and resume the selected thread. If we&#8217;re suspending the thread, this is simply a case of calling the <code>Suspend()<\/code> method of the <code>Thread<\/code> object (if the thread is already suspended, this will be ignored), but if we&#8217;re restarting it, we need to check that the thread has actually been suspended before calling its <code>Resume<\/code> method:<\/p>\n<pre>case (Keys.F7):\r\n\t{\r\n\t\tselectedString.Thread.Suspend();\r\n\t\tbreak;\r\n\t}\r\ncase (Keys.F8):\r\n\t{\r\n\t\tif ((selectedString.Thread.ThreadState &amp; ThreadState.Suspended) == ThreadState.Suspended) selectedString.Thread.Resume();\r\n\t\tbreak;\r\n\t}\r\n\t}<\/pre>\n<h3>Conclusion<\/h3>\n<p>In this article, we&#8217;ve looked at the basics of asynchronous processing in .NET, including a sample Windows Forms application that allowed the user to perform simple manipulation of a number of threads. In Part 2, we&#8217;ll look in more detail at synchronization in .NET, and also look at the features provided for asynchronous programming in .NET 2.0.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Windows Forms applications, we often need to perform some complex processing in the background, while still continuing with other tasks, such as monitoring user input and updating the user interface. For example, if you think of a web browser, it fetches and renders a web page without the main form hanging, so we can still press the Stop or Back buttons without waiting for the current page to load. This is achieved through asynchronous processing; that is, processing that takes advantage of Windows&#8217; multi-threading abilities to perform (as it appears to the user) two tasks at the same time.&hellip;<\/p>\n","protected":false},"author":103084,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[4143,4365,4178,4364,4366,4316],"coauthors":[42900],"class_list":["post-130","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-net","tag-asynchronous-processing","tag-bi","tag-monitoring","tag-multithreading","tag-windows-forms"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/130","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/103084"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=130"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/130\/revisions"}],"predecessor-version":[{"id":90767,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/130\/revisions\/90767"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=130"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=130"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=130"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=130"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}