Multithreaded Applications (C# and Visual Basic)
With Visual Basic and C#, you can write applications that perform multiple tasks at the same time. Tasks with the potential of holding up other tasks can execute on separate threads, a process known as multithreading or free threading.
Applications that use multithreading are more responsive to user input because the user interface stays active as processor-intensive tasks execute on separate threads. Multithreading is also useful when you create scalable applications, because you can add threads as the workload increases.
Visual Studio 2010 and .NET Framework 4 enhance support for parallel programming by providing a new runtime, new class library types, and new diagnostic tools. For more information, see Parallel Programming in the .NET Framework.
The most reliable way to create a multithreaded application is to use the BackgroundWorker component. This class manages a separate thread dedicated to processing the method that you specify. For an example, see Walkthrough: Multithreading with the BackgroundWorker Component (C# and Visual Basic).
To start an operation in the background, create a BackgroundWorker and listen for events that report the progress of your operation and signal when your operation is finished. You can create the BackgroundWorker object programmatically, or you can drag it onto a form from the Components tab of the Toolbox. If you create the BackgroundWorker in the Forms Designer, it appears in the Component Tray, and its properties are displayed in the Properties window.
To set up for a background operation, add an event handler for the DoWork event. Call your time-consuming operation in this event handler.
To start the operation, call RunWorkerAsync. To receive notifications of progress updates, handle the ProgressChanged event. To receive a notification when the operation is completed, handle the RunWorkerCompleted event.
The methods that handle the ProgressChanged and RunWorkerCompleted events can access the application's user interface, because those events are raised on the thread that called the RunWorkerAsync method. However, the DoWork event handler cannot work with any user-interface objects because it runs on the background thread.
If you need more control over the behavior of your application's threads, you can manage the threads yourself. However, realize that writing correct multithreaded applications can be difficult: Your application may stop responding or experience transient errors caused by race conditions. For more information, see Thread-Safe Components.
You create a new thread by declaring a variable of type Thread and calling the constructor, providing the name of the procedure or method that you want to execute on the new thread. The following code provides an example.
Starting and Stopping Threads
To start the execution of a new thread, use the Start method, as shown in the following code.
To stop the execution of a thread, use the Abort method, as shown in the following code.
Besides starting and stopping threads, you can also pause threads by calling the Sleep or Suspend method, resume a suspended thread by using the Resume method, and destroy a thread by using the Abort method
The following table shows some of the methods that you can use to control individual threads.
Causes a thread to start to run.
Pauses a thread for a specified time.
Pauses a thread when it reaches a safe point.
Stops a thread when it reaches a safe point.
Restarts a suspended thread
Causes the current thread to wait for another thread to finish. If used with a time-out value, this method returns True if the thread finishes in the allocated time.
Most of these methods are self-explanatory, but the concept of safe points may be new to you. Safe points are locations in code where it is safe for the common language runtime to perform automatic garbage collection, the process of releasing unused variables and reclaiming memory. When you call the Abort or Suspend method of a thread, the common language runtime analyzes the code and determines the location of an appropriate location for the thread to stop running.
Threads also contain several useful properties, as shown in the following table:
Contains the value True if a thread is active.
Gets or sets a Boolean that indicates if a thread is or should be a background thread. Background threads are like foreground threads, but a background thread does not prevent a process from stopping. Once all foreground threads that belong to a process have stopped, the common language runtime ends the process by calling the Abort method on background threads that are still alive.
Gets or sets the name of a thread. Most frequently used to discover individual threads when you debug.
Gets or sets a value that is used by the operating system to prioritize thread scheduling.
Gets or sets the threading model used for a particular thread. Threading models are important when a thread calls unmanaged code.
Contains a value that describes a thread's state or states.
Every thread has a priority property that determines how big or small a slice of processor time it has to execute. The operating system allocates longer time slices to high-priority threads and shorter time slices to low-priority threads. New threads are created with the value of Normal, but you can change the Priority property to any value in the ThreadPriority enumeration.
See ThreadPriority for a detailed description of the various thread priorities.
A foreground thread runs indefinitely, whereas a background thread stops as soon as the last foreground thread has stopped. You can use the IsBackground property to determine or change the background status of a thread.
While multithreading is best suited to running procedures and class methods, you can also use it with forms and controls. If you do so, be aware of the following points:
Whenever possible, execute the methods of a control only on the thread with which it was created. If you must call a method of a control from another thread, you must use Invoke to call the method.
Do not use the SyncLock (Visual Basic) or lock (C#) statement to lock threads that manipulate controls or forms. Because the methods of controls and forms sometimes call back to a calling procedure, you can end up inadvertently creating a deadlock—a situation in which two threads wait for each other to release the lock, causing the application to halt.