一尘不染

有没有一种方法可以无限期地暂停线程?

c#

我在业余时间一直在开发Web爬网.NET应用程序,而我想包含的该应用程序的功能之一是用于暂停特定线程的暂停按钮。

我是多线程技术的新手,但我一直无法找出无限期暂停当前支持的线程的方法。我不记得确切的类/方法,但是我知道有一种方法可以执行此操作,但是.NET框架已将其标记为过时。

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

我最近没有太多时间来开发此应用程序,而我上次接触它的时间是在.NET 2.0框架中。我愿意接受.NET
3.5框架中存在的任何新功能(如果有),但是我想知道在2.0框架中也可以使用的解决方案,因为这是我在工作中使用的,并且知道以防万一。


阅读 266

收藏
2020-05-19

共1个答案

一尘不染

永远不要使用Thread.Suspend。它的主要问题是,在挂起线程时,有99%的时间您不知道该线程在做什么。如果该线程持有锁,则可以更容易陷入死锁等情况。请记住,您正在调用的代码可能正在幕后获取/释放锁。Win32具有类似的API:SuspendThreadResumeThread。以下文档为SuspendThreadAPI的危害提供了很好的摘要:

http://msdn.microsoft.com/zh-
cn/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..
        }
    }
}
2020-05-19