有没有办法无限期地暂停一个线程?
我一直在空闲时间在网上爬行.NET应用程序,我想包括这个应用程序的function之一是暂停button来暂停特定的线程。
我对于multithreading相对来说比较新,而且我还没有find一种无限期地暂停线程的方法。 我不记得确切的类/方法,但我知道有一种方法可以做到这一点,但它已被标记为过时的.NET框架。
有没有什么好的通用目的的方式来无限期地暂停C#.NET中的工作线程。
最近我没有太多的时间来处理这个应用程序,最后一次碰到它是在.NET 2.0框架。 我接受.NET 3.5框架中的任何新function(如果有的话),但是我想知道在2.0框架下工作的解决scheme,因为这是我在工作中使用的方法,以防万一。
永远不要使用Thread.Suspend
。 它的主要问题是,99%的时间你不知道什么线程正在做什么,当你暂停它。 如果该线程持有一个锁,则可以更容易地陷入死锁状态等。请记住,您正在调用的代码可能是在幕后获取/释放锁。 Win32有一个类似的API: SuspendThread
和ResumeThread
。 下面的SuspendThread
文档给出了API的危险性的一个很好的总结:
http://msdn.microsoft.com/en-us/library/ms686345(VS.85).aspx
这个函数主要是为debugging器devise的。 它不打算用于线程同步。 在拥有同步对象(如互斥锁或关键部分)的线程上调用SuspendThread,如果调用线程尝试获取暂停线程拥有的同步对象,则会导致死锁。 为了避免这种情况,应用程序中不是debugging器的线程应该发信号让另一个线程自行挂起。 目标线程必须devise为监视此信号并作出适当的响应。
无限期挂起线程的正确方法是使用ManualResetEvent
。 线程很可能循环,执行一些工作。 暂停线程的最简单方法是让线程每次迭代检查事件,如下所示:
while (true) { _suspendEvent.WaitOne(Timeout.Infinite); // Do some work... }
你指定一个无限的超时时间,所以当事件没有发信号时,线程将无限期地阻塞,直到事件发出信号,线程将在哪一点停止。
你会像这样创build事件:
ManualResetEvent _suspendEvent = new ManualResetEvent(true);
true
参数告诉事件在信号状态下开始。
当你想暂停线程时,你需要做以下的事情:
_suspendEvent.Reset();
并恢复线程:
_suspendEvent.Set();
您可以使用类似的机制来指示线程退出并等待两个事件,检测哪个事件被发送。
为了好玩,我将提供一个完整的例子:
public class Worker { ManualResetEvent _shutdownEvent = new ManualResetEvent(false); ManualResetEvent _pauseEvent = new ManualResetEvent(true); Thread _thread; public Worker() { } public void Start() { _thread = new Thread(DoWork); _thread.Start(); } public void Pause() { _pauseEvent.Reset(); } public void Resume() { _pauseEvent.Set(); } public void Stop() { // Signal the shutdown event _shutdownEvent.Set(); // Make sure to resume any paused threads _pauseEvent.Set(); // Wait for the thread to exit _thread.Join(); } public void DoWork() { while (true) { _pauseEvent.WaitOne(Timeout.Infinite); if (_shutdownEvent.WaitOne(0)) break; // Do the work here.. } } }
C# ebook中的线程总结Thread.Suspend和Thread.Resume如下:
弃用的Suspend和Resume方法有两种模式 – 危险和无用!
本书build议使用一个同步构造,如AutoResetEvent或Monitor.Wait来执行线程挂起和恢复。
我刚刚实现了一个循环传递给构造函数的动作的LoopingThread
类。 这是基于布兰农的职位。 我已经把一些其他的东西,像WaitForPause()
, WaitForStop()
,和一个TimeBetween
属性,表明在下一个循环之前应该等待的时间。
我也决定把while循环改成do-while-loop。 这会给我们一个连续的Start()
和Pause()
的确定性行为。 对于确定性,我的意思是,在Start()
命令之后至less执行一次该操作。 在Brannon的实施中,情况可能不是这样。
我忽略了一些事情的根源。 像“检查线程是否已经启动”,或者IDisposable
模式。
public class LoopingThread { private readonly Action _loopedAction; private readonly AutoResetEvent _pauseEvent; private readonly AutoResetEvent _resumeEvent; private readonly AutoResetEvent _stopEvent; private readonly AutoResetEvent _waitEvent; private readonly Thread _thread; public LoopingThread (Action loopedAction) { _loopedAction = loopedAction; _thread = new Thread (Loop); _pauseEvent = new AutoResetEvent (false); _resumeEvent = new AutoResetEvent (false); _stopEvent = new AutoResetEvent (false); _waitEvent = new AutoResetEvent (false); } public void Start () { _thread.Start(); } public void Pause (int timeout = 0) { _pauseEvent.Set(); _waitEvent.WaitOne (timeout); } public void Resume () { _resumeEvent.Set (); } public void Stop (int timeout = 0) { _stopEvent.Set(); _resumeEvent.Set(); _thread.Join (timeout); } public void WaitForPause () { Pause (Timeout.Infinite); } public void WaitForStop () { Stop (Timeout.Infinite); } public int PauseBetween { get; set; } private void Loop () { do { _loopedAction (); if (_pauseEvent.WaitOne (PauseBetween)) { _waitEvent.Set (); _resumeEvent.WaitOne (Timeout.Infinite); } } while (!_stopEvent.WaitOne (0)); } }
除了上面的build议,我想添加一个技巧。 在某些情况下,使用BackgroundWorker可以简化您的代码(特别是当您使用匿名方法来定义DoWork和其他事件时)。
根据别人的说法 – 不要这样做。 你真正想做的是“暂停工作”,让你的线程自由地漫游。 你能给我们一些关于你想暂停的线程的更多细节吗? 如果你没有启动线程,你绝对不应该考虑暂停它 – 它不是你的。 如果是你的线索,那么我build议你暂时停下来,等待更多的工作。 在他的回应中,布兰农对这个选项有一些很好的build议。 或者,只要让它结束; 当你需要的时候旋转一个新的。
如果没有同步要求:
Thread.Sleep(Timeout.Infinite);
Suspend()和Resume()可能被删除,但是它们绝不是无用的。 例如,如果你有一个线程做了一个冗长的工作来改变数据,并且用户希望停止它,他点击一个button。 当然,你需要请求validation,但同时你不希望这个线程继续改变数据,如果用户决定他真的要中止。 在等待用户单击确认对话框中的“是”或“否”button时,挂起“线程”是防止其更改数据的唯一方法,然后再发出指定的放弃事件以使其停止。 对于具有一个循环的简单线程,事件可能会很好,但复杂处理的复杂线程是另一个问题。 当然,Suspend() 不能用于同步,因为它的用处不在于这个函数。
只是我的意见。