C# 有没有办法无限期地暂停一个线程?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/142826/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 15:24:41  来源:igfitidea点击:

Is there a way to indefinitely pause a thread?

提问by Dan Herbert

I've been working on a web crawling .NET app in my free time, and one of the features of this app that I wanted to included was a pause button to pause a specific thread.

我在空闲时间一直在开发一个网络爬行 .NET 应用程序,我想要包含的这个应用程序的功能之一是一个暂停按钮来暂停特定线程。

I'm relatively new to multi-threading and I haven't been able to figure out a way to pause a thread indefinitely that is currently supported. I can't remember the exact class/method, but I know there is a way to do this but it has been flagged as obsolete by the .NET framework.

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

Is there any good general purpose way to indefinitely pause a worker thread in C# .NET.

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

I haven't had a lot of time lately to work on this app and the last time I touched it was in the .NET 2.0 framework. I'm open to any new features (if any) that exist in the .NET 3.5 framework, but I'd like to know of solution that also works in the 2.0 framework since that's what I use at work and it would be good to know just in case.

我最近没有太多时间来处理这个应用程序,我最后一次接触它是在 .NET 2.0 框架中。我对 .NET 3.5 框架中存在的任何新功能(如果有的话)持开放态度,但我想知道在 2.0 框架中也适用的解决方案,因为这是我在工作中使用的,这会很好知道以防万一。

采纳答案by Brannon

Never, ever use Thread.Suspend. The major problem with it is that 99% of the time you can't know what that thread is doing when you suspend it. If that thread holds a lock, you make it easier to get into a deadlock situation, etc. Keep in mind that code you are calling may be acquiring/releasing locks behind the scenes. Win32 has a similar API: SuspendThreadand ResumeThread. The following docs for SuspendThreadgive a nice summary of the dangers of the API:

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

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

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

This function is primarily designed for use by debuggers. It is not intended to be used for thread synchronization. Calling SuspendThread on a thread that owns a synchronization object, such as a mutex or critical section, can lead to a deadlock if the calling thread tries to obtain a synchronization object owned by a suspended thread. To avoid this situation, a thread within an application that is not a debugger should signal the other thread to suspend itself. The target thread must be designed to watch for this signal and respond appropriately.

此函数主要是为调试器设计的。它不打算用于线程同步。如果调用线程尝试获取挂起线程拥有的同步对象,则在拥有同步对象(例如互斥锁或临界区)的线程上调用 SuspendThread 可能会导致死锁。为避免这种情况,应用程序中不是调试器的线程应该通知另一个线程挂起自己。目标线程必须设计为监视此信号并做出适当的响应。

The proper way to suspend a thread indefinitely is to use a ManualResetEvent. The thread is most likely looping, performing some work. The easiest way to suspend the thread is to have the thread "check" the event each iteration, like so:

无限期挂起线程的正确方法是使用ManualResetEvent. 该线程很可能在循环,执行一些工作。挂起线程的最简单方法是让线程在每次迭代时“检查”事件,如下所示:

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

    // Do some work...
}

You specify an infinite timeout so when the event is not signaled, the thread will block indefinitely, until the event is signaled at which point the thread will resume where it left off.

您指定无限超时,因此当事件没有发出信号时,线程将无限期地阻塞,直到事件发出信号,此时线程将从停止的位置恢复。

You would create the event like so:

您可以像这样创建事件:

ManualResetEvent _suspendEvent = new ManualResetEvent(true);

The trueparameter tells the event to start out in the signaled state.

true参数告诉事件以信号状态开始。

When you want to pause the thread, you do the following:

当您想暂停线程时,请执行以下操作:

_suspendEvent.Reset();

And to resume the thread:

并恢复线程:

_suspendEvent.Set();

You can use a similar mechanism to signal the thread to exit and wait on both events, detecting which event was signaled.

您可以使用类似的机制来通知线程退出并等待两个事件,检测哪个事件被发出信号。

Just for fun I'll provide a complete example:

只是为了好玩,我将提供一个完整的示例:

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

回答by Shabbyrobe

The Threading in C#ebook summarises Thread.Suspend and Thread.Resume thusly:

C#电子书中的线程对Thread.Suspend 和 Thread.Resume 进行了总结:

The deprecated Suspend and Resume methods have two modes – dangerous and useless!

已弃用的 Suspend 和 Resume 方法有两种模式——危险和无用!

The book recommends using a synchronization construct such as an AutoResetEventor Monitor.Waitto perform thread suspending and resuming.

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

回答by Lex Li

Beside suggestions above, I'd like to add one tip. In some cases, use BackgroundWorker can simplify your code (especially when you use anonymous method to define DoWork and other events of it).

除了上面的建议,我想添加一个提示。在某些情况下,使用 BackgroundWorker 可以简化您的代码(尤其是当您使用匿名方法定义 DoWork 和它的其他事件时)。

回答by Bruce

In line with what the others said - don't do it. What you really want to do is to "pause work", and let your threads roam free. Can you give us some more details about the thread(s) you want to suspend? If you didn't start the thread, you definitely shouldn't even consider suspending it - its not yours. If it is your thread, then I suggest instead of suspending it, you just have it sit, waiting for more work to do. Brannon has some excellent suggestions for this option in his response. Alternatively, just let it end; and spin up a new one when you need it.

根据其他人所说的 - 不要这样做。您真正想要做的是“暂停工作”,让您的线程自由漫游。您能否提供有关您要暂停的线程的更多详细信息?如果你没有启动线程,你绝对不应该考虑挂起它——它不是你的。如果它是您的线程,那么我建议您不要挂起它,而是让它坐着,等待更多的工作要做。Brannon 在他的回复中对这个选项提出了一些很好的建议。或者,就让它结束;并在需要时启动一个新的。

回答by ThunderGr

The Suspend() and Resume() may be depricated, however they are in no way useless. If, for example, you have a thread doing a lengthy work altering data, and the user wishes to stop it, he clicks on a button. Of course, you need to ask for verification, but, at the same time you do not want that thread to continue altering data, if the user decides that he really wants to abort. Suspending the Thread while waiting for the user to click that Yes or No button at the confirmation dialog is the onlyway to prevent it from altering the data, before you signal the designated abort event that will allow it to stop. Events may be nice for simple threads having one loop, but complicated threads with complex processing is another issue. Certainly, Suspend() must neverbe used for syncronising, since its usefulness is not for this function.

Suspend() 和 Resume() 可能会被贬低,但它们绝不是无用的。例如,如果您有一个线程正在执行一项冗长的数据更改工作,而用户希望停止它,他可以单击一个按钮。当然,您需要请求验证,但同时您不希望该线程继续更改数据,如果用户决定他真的想中止。在等待用户单击确认对话框中的“是”或“否”按钮时暂停线程是防止它更改数据的唯一方法,然后再发出信号以允许它停止的指定中止事件。事件对于具有一个循环的简单线程可能很好,但具有复杂处理的复杂线程是另一个问题。当然,Suspend() 绝不能用于同步,因为它的用处不是用于此功能。

Just my opinion.

只是我的观点。

回答by Matthias

I just implemented a LoopingThreadclass which loops an action passed to the constructor. It is based on Brannon's post. I've put some other stuff into that like WaitForPause(), WaitForStop(), and a TimeBetweenproperty, that indicates the time that should be waited before next looping.

我刚刚实现了一个LoopingThread类,它循环传递给构造函数的动作。它基于布兰农的帖子。我已经将一些其他内容放入其中,例如WaitForPause(),WaitForStop()和一个TimeBetween属性,该属性指示在下一次循环之前应该等待的时间。

I also decided to change the while-loop to an do-while-loop. This will give us a deterministic behavior for a successive Start()and Pause(). With deterministic I mean, that the action is executed at least once after a Start()command. In Brannon's implementation this might not be the case.

我还决定将 while 循环更改为 do-while 循环。这将为我们提供连续Start()和的确定性行为Pause()。确定性我的意思是,该操作在Start()命令后至少执行一次。在布兰农的实现中,情况可能并非如此。

I omitted some things for the root of the matter. Things like "check if the thread was already started", or the IDisposablepattern.

我省略了一些事情的根源。诸如“检查线程是否已经启动”或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));
  }
}

回答by Alberto

If there are no synchronization requirements:

如果没有同步要求:

Thread.Sleep(Timeout.Infinite);

Thread.Sleep(Timeout.Infinite);