C#中线程的终止问题


C#中线程的终止可以利用线程的abort()或是Interrupt()函数,但是这两个函数的缺点是关闭的不够优雅,也就是说不管线程在做什么,都直接进行关闭操作。

而msdn上面给我们提供了一个优雅的关闭线程的方式,可以让线程函数通过判断自行退出线程。

代码如下:

 1 using System;
 2 using System.Threading;
 3 
 4 public class Worker
 5 {
 6     // This method will be called when the thread is started.
 7     public void DoWork()
 8     {
 9         while (!_shouldStop)
10         {
11             Console.WriteLine("worker thread: working...");
12         }
13         Console.WriteLine("worker thread: terminating gracefully.");
14     }
15     public void RequestStop()
16     {
17         _shouldStop = true;
18     }
19     // Volatile is used as hint to the compiler that this data
20     // member will be accessed by multiple threads.
21     private volatile bool _shouldStop;
22 }
23 
24 public class WorkerThreadExample
25 {
26     static void Main()
27     {
28         // Create the thread object. This does not start the thread.
29         Worker workerObject = new Worker();
30         Thread workerThread = new Thread(workerObject.DoWork);
31 
32         // Start the worker thread.
33         workerThread.Start();
34         Console.WriteLine("main thread: Starting worker thread...");
35 
36         // Loop until worker thread activates.
37         while (!workerThread.IsAlive);
38 
39         // Put the main thread to sleep for 1 millisecond to
40         // allow the worker thread to do some work:
41         Thread.Sleep(1);
42 
43         // Request that the worker thread stop itself:
44         workerObject.RequestStop();
45 
46         // Use the Join method to block the current thread 
47         // until the object's thread terminates.
48         workerThread.Join();
49         Console.WriteLine("main thread: Worker thread has terminated.");
50     }
51 }

输出的结果为:

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

分析上面的代码可以知道,通过在主线程中设置_shouldStop 变量,在workerThread 线程中判断_shouldStop变量来控制线程的结束,而在主线程中通过workerThread线程的jion()函数来等待workThread线程的结束。

正常情况下通过上述方法就能够成功而优雅的结束一个线程,但是在写程序时遇到一个问题。

 

非UI线程代码如下:

 1         /// <summary>
 2         /// 轨迹回放的线程
 3         /// </summary>
 4         private void TrialReplayService()
 5         {
 6             mapControl1.Map.TrackingLayer.Clear();//清空跟踪图层中的内容
 7             GlobalVariable.m_BusID.Clear();
 8             
 9             MapsRun mapRun = new MapsRun(workspace1, mapControl1);
10             // Create an instance of StreamReader to read from a file.
11             // The using statement also closes the StreamReader.
12             using (StreamReader sr = new StreamReader("RecordFile.txt"))
13             {
14                 String line;
15 
16                 while ((line = sr.ReadLine()) != null&&!m_IsStop)
17                 {
18                     StringToBusInfo businfo = new StringToBusInfo(line);
19                     string carNum = businfo.Get_CarNum();
20                     if (carNum == m_SelectCar)
21                     {
22                         mapRun.ShowRealInfo(line);
23                         this.dataGridView2.Invoke(new InsertDataGridViewHandler(InsertDataGridView), line);
24                         Thread.Sleep(50);
25                     }
28                 }
29             }
30             
31         }

而主线程中启动和结束TrialReplayService线程(就是利用上面的代码创建的线程)的代码如下:

 1 /// <summary>
 2         /// 启动轨迹回放的功能
 3         /// </summary>
 4         /// <param name="sender"></param>
 5         /// <param name="e"></param>
 6         private void button1_Click(object sender, EventArgs e)
 7         {
 8              m_threadMonitor = new Thread(new ThreadStart(m_TrailPlayback.TrailPlaybackService));
 9              m_threadMonitor.Start();
10         }
11 
12 /// <summary>
13         /// 窗体的结束时的操作
14         /// </summary>
15         /// <param name="sender"></param>
16         /// <param name="e"></param>
17         private void TrailPlaybackForm_FormClosing(object sender, FormClosingEventArgs e)
18         {
19 
20             if (m_threadMonitor.IsAlive)
21             {
22                 m_TrailPlayback.RequestStop();
23             
24                 m_threadMonitor.Join(); 
27             }
28 31             mapControl1.Dispose();
32             workspace1.Dispose();
33 
34             //初始化主窗体中的地图
35             MapsRun.InitialMap(mainWorkSpace, mainMapControl);
36         }

这些代码为项目中摘取,因此可能有些变量没有定义。代码8~9行初始化了一个线程变量m_threadMonitor,并且启动了该线程。20~24行,我们试图关闭m_threadMonitor线程,首先通过线程的IsAlive来判断线程是否存活,如果存活,则通过自定义的RequestStop()函数来设置变量来终止线程,而24行,则是等待线程的结束。而这时产生的问题是程序卡在jion()函数的位置,不再向下执行。

经过将近一天的调试终于解决了问题。首先通过不断的调试(虽然此时已经不能通过单步调试定位到具体的错误位置,但是可以通过排除法来定位,首先单独创建一个程序来测试这种终止线程的方法是否正确,判断后是正确的,然后开始测试循环中的各个语句,看是因为哪条语句的原因造成了这种错误),将问题定位到了:this.dataGridView2.Invoke(new InsertDataGridViewHandler(InsertDataGridView), line)这条语句。如果给这条语句注释了,线程则能够正常关闭,否则关闭线程时会卡死在UI线程jion()函数的位置。

确定了是这条语句的原因后,查询关于control.Invole()的资料,找到了问题的原因。control的Invoke()操作是多线程操作控件的一种方式,为了线程的安全性,通过Invoke调用的函数仍然是在UI线程中执行的,而由于invoke的特性m_threadMonitor线程在调用invoke函数时会发生阻塞,直到invoke调用的函数返回后,才会继续执行invoke函数后面的代码。而问题的产生在于,当我们想结束线程时,执行到UI线程中的代码 m_threadMonitor.Join()时,它在等待线程m_threadMonitor的结束,而m_threadMonitor线程在等待Invoke函数的返回,Invoke函数需要再UI线程中执行,而此时UI线程在执行m_threadMonitor.Join()代码,因此Invoke函数在等待m_threadMonitor.Join()的结束。这样就形成了一个死循环,m_threadMonitor.Join()等待m_threadMonitor结束,m_threadMonitor等待invoke返回,invoke等待m_threadMonitor.Join()的结束,因此代码会卡死在这里。

解决这个问题的方法是使用control.BeginInvoke()函数,因为它是异步的操作,也就是说m_threadMonitor线程在调用BeginInvoke函数后不会等待它调用函数的返回,而是直接执行BeginInvoke后面的代码,这样就不会产生死循环,也就能够顺利的关闭m_threadMonitor线程了。

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM