此示例演示如何创建辅助线程,并用它与主线程并行执行处理。还将演示如何使一个线程等待另一个线程,并正确地终止线程。有关多线程处理的背景信息,请参见
该示例创建一个名为 Worker
的类,该类包含辅助线程将执行的方法 DoWork
。这实际上是辅助线程的 Main
函数。辅助线程将通过调用此方法来开始执行,并在此方法返回时自动终止。DoWork
方法如下所示:
C# | ![]() |
---|---|
public void DoWork() { while (!_shouldStop) { Console.WriteLine("worker thread: working..."); } Console.WriteLine("worker thread: terminating gracefully."); } |
Worker
类包含另一个方法,该方法用于通知 DoWork
它应当返回。此方法名为 RequestStop
,如下所示:
C# | ![]() |
---|---|
public void RequestStop() { _shouldStop = true; } |
RequestStop
方法只是将 true 赋给 _shouldStop
数据成员。由于此数据成员由 DoWork
方法来检查,因此这会间接导致 DoWork
返回,从而终止辅助线程。但是,需要注意:DoWork
和 RequestStop
将由不同线程执行。DoWork
由辅助线程执行,而 RequestStop
由主线程执行,因此 _shouldStop
数据成员声明为 volatile,如下所示:
C# | ![]() |
---|---|
private volatile bool _shouldStop; |
volatile 关键字用于通知编译器,将有多个线程访问 _shouldStop
数据成员,因此它不应当对此成员的状态做任何优化假设。有关更多信息,请参见 volatile(C# 参考)。
通过将 volatile 与 _shouldStop
数据成员一起使用,可以从多个线程安全地访问此成员,而不需要使用正式的线程同步技术,但这仅仅是因为 _shouldStop
是 bool。这意味着只需要执行单个原子操作就能修改 _shouldStop
。但是,如果此数据成员是类、结构或数组,那么,从多个线程访问它可能会导致间歇的数据损坏。假设有一个更改数组中的值的线程。Windows 定期中断线程,以便允许其他线程执行,因此线程会在分配某些数组元素之后和分配其他元素之前被中断。这意味着,数组现在有了一个程序员从不想要的状态,因此,读取此数组的另一个线程可能会失败。
在实际创建辅助线程之前,Main
函数会创建一个 Worker
对象和 Worker.DoWork
方法的引用传递给 Thread 构造函数,来将该方法用作入口点,如下所示:
C# | ![]() |
---|---|
Worker workerObject = new Worker(); Thread workerThread = new Thread(workerObject.DoWork); |
此时,尽管辅助线程对象已存在并已配置,但尚未创建实际的辅助线程。只有当 Main
调用
C# | ![]() |
---|---|
workerThread.Start(); |
此时,系统将启动辅助线程的执行,但这是在与主线程异步执行的。这意味着 Main
函数将在辅助线程进行初始化的同时继续执行代码。为了保证 Main
函数不会尝试在辅助线程有机会执行之前将它终止,Main
函数将一直循环,直到辅助线程对象的
C# | ![]() |
---|---|
while (!workerThread.IsAlive); |
下一步,通过调用 DoWork
函数在 Main
函数执行其他任何命令之前,在 DoWork
方法内部执行若干次循环:
C# | ![]() |
---|---|
Thread.Sleep(1); |
在 1 毫秒之后,Main
将通知辅助线程对象,它应当使用 Worker.RequestStop
方法(前面已介绍)自行终止:
C# | ![]() |
---|---|
workerObject.RequestStop(); |
还可以通过调用
最后,Main
函数对辅助线程对象调用
C# | ![]() |
---|---|
workerThread.Join(); |
此时,只有执行 Main
的主线程还存在。它会显示一条最终消息,然后返回,从而使主线程也终止。
下面显示了完整的示例:
示例
C# | ![]() |
---|---|
using System; using System.Threading; public class Worker { // This method will be called when the thread is started. public void DoWork() { while (!_shouldStop) { Console.WriteLine("worker thread: working..."); } Console.WriteLine("worker thread: terminating gracefully."); } public void RequestStop() { _shouldStop = true; } // Volatile is used as hint to the compiler that this data // member will be accessed by multiple threads. private volatile bool _shouldStop; } public class WorkerThreadExample { static void Main() { // Create the thread object. This does not start the thread. Worker workerObject = new Worker(); Thread workerThread = new Thread(workerObject.DoWork); // Start the worker thread. workerThread.Start(); Console.WriteLine("main thread: Starting worker thread..."); // Loop until worker thread activates. while (!workerThread.IsAlive); // Put the main thread to sleep for 1 millisecond to // allow the worker thread to do some work: Thread.Sleep(1); // Request that the worker thread stop itself: workerObject.RequestStop(); // Use the Join method to block the current thread // until the object's thread terminates. workerThread.Join(); Console.WriteLine("main thread: Worker thread has terminated."); } } |
示例输出
main thread: starting worker thread... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: terminating gracefully... main thread: worker thread has terminated |
请参见
任务
参考
线程处理(C# 编程指南)使用线程处理(C# 编程指南)
volatile(C# 参考)