2008-09-27 81 views
32

我在空闲时间一直在研究网络爬行.NET应用程序,而我想包含的这个应用程序的功能之一是暂停按钮来暂停特定线程。有没有办法无限期地暂停一个线程?

我对多线程相对来说比较陌生,我一直无法找出一种无限期地暂停当前支持的线程的方法。我不记得确切的类/方法,但我知道有一种方法可以做到这一点,但它已被.NET框架标记为过时。

有没有什么好的通用目的的方式来无限期地暂停C#.NET中的工作线程。

我最近没有很多时间在这个应用程序上工作,而最后一次碰触它是在.NET 2.0框架中。我对.NET 3.5框架中存在的任何新功能(如果有的话)都很开放,但我想知道在2.0框架中也可以工作的解决方案,因为这是我在工作中使用的方法,知道以防万一。

回答

90

永远不会使用Thread.Suspend。它的主要问题是99%的时间你不知道当你挂起它时该线程在做什么。如果该线程持有一个锁,则可以更容易进入死锁状态等。请记住,您正在调用的代码可能会在幕后获取/释放锁。 Win32有一个类似的API:SuspendThreadResumeThread。为SuspendThread下面的文档给API的危险的一个很好的总结:

http://msdn.microsoft.com/en-us/library/ms686345(VS.85).aspx

此功能主要是用来调试程序的使用。它不打算用于线程同步。在拥有同步对象(如互斥锁或临界区)的线程上调用SuspendThread,如果调用线程试图获取暂停线程拥有的同步对象,则可能导致死锁。为了避免这种情况,应用程序中不是调试器的线程应该发信号通知其他线程自行挂起。目标线程必须设计为监视此信号并进行适当的响应。

无限期暂停线程的正确方法是使用ManualResetEvent。线程很可能循环,执行一些工作。暂停线程,最简单的方法就是让线程“检查”了事件的每一次迭代中,像这样:

while (true) 
{ 
    _suspendEvent.WaitOne(Timeout.Infinite); 

    // Do some work... 
} 

您指定一个无限超时,所以当没有信号的情况下,该线程将无限期阻塞,直到该事件将在该点停止的地方恢复。

您可以创建事件,像这样:

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.. 
     } 
    } 
} 
15

Threading in C#电子书总结Thread.Suspend和Thread.resume正是如此:

弃用的暂停和恢复有两种方式 - 危险和无用!

本书建议使用同步构造,例如AutoResetEventMonitor.Wait来执行线程挂起和恢复。

+0

我得去和阅读电子书。我从来没有遇到挂起/恢复自己的问题。 – 2008-09-27 03:02:27

0

除了以上的建议,我想补充一个提示。在某些情况下,使用BackgroundWorker可以简化您的代码(特别是当您使用匿名方法来定义DoWork及其它事件时)。

0

根据别人的说法 - 不要这样做。你真正想要做的是“暂停工作”,并让你的线程自由地漫游。你能给我们一些关于你想暂停的线程的更多细节吗?如果你没有启动线程,你绝对不应该考虑暂停它 - 它不是你的。如果是你的线索,那么我建议你暂时停下来,等待更多的工作要做。在他的回应中,布兰农对这个选项有一些很好的建议。或者,只要让它结束;并在需要时启动一个新的。

-1

Suspend()和Resume()可能被删除,但它们绝不是无用的。 例如,如果你有一个线程做了一个长时间的工作来改变数据,并且用户希望停止它,他点击一个按钮。当然,您需要请求验证,但同时您不希望该线程继续更改数据,如果用户决定确实想要中止。 在等待用户在确认对话框中单击“是”或“否”按钮时,挂起线程是仅在发出指定的放弃事件时才允许其停止的方法,以防止其更改数据。 事件对于具有一个循环的简单线程来说可能很好,但是具有复杂处理的复杂线程是另一个问题。 当然,Suspend()必须使用从不使用进行同步,因为它的用处不在于此功能。

只是我的意见。

1

我刚刚实现了一个LoopingThread类,它循环传递给构造函数的动作。它基于Brannon的帖子。我已经将其他一些东西放入了像WaitForPause(),WaitForStop()TimeBetween属性,它表示在下一次循环之前应该等待的时间。

我还决定将while循环更改为do-while-loop。这会给我们一个连续的Start()Pause()的确定性行为。对于确定性,我的意思是,这个动作至少会在执行Start()命令后执行一次。在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)); 
    } 
} 
0

如果没有同步要求:

Thread.Sleep(Timeout.Infinite);

相关问题