从主UI线程上的按钮挂起子线程

时间:2011-12-12 22:12:49

标签: c#

我找到了这个话题How to suspend a thread by its name from the main thread?,但对于我想要实现的目标却没有令人满意的答案。

我正在使用线程和WatiN类同时在同一窗体中的两个浏览器上执行事件。

我想从主UI线程中按下其中一个浏览器中的暂停按钮,通过导出按下暂停按钮的浏览器的控件名称,使用该名称来确定哪个子-thread与其运行逻辑相关联,并暂停运行逻辑,直到按下播放按钮。

现在,我们在代码和技术方面做得非常成功,应该有办法做到这一点。

您怎么看?

研究理念:

务实地创建ManualResetEvent并命名它,使用UI暂停按钮来获取打开的浏览器控件名称,该名称类似于子线程和浏览器控件名称(例如browser_5& thread_5)以某种方式定位在MRE上在子线程中,关闭门以暂停逻辑。 (但这可以在主UI线程的子线程上完成吗?)

2 个答案:

答案 0 :(得分:3)

不要使用thread.Suspend

乍一看,您似乎可以use thread.Suspend() to pause itthread.Resume() to unpause it。但这不是一个好主意。除非您打算终止该线程的AppDomain,否则请参阅the MSDN article for thread.Suspend for why you should never use it

  

不要使用Suspend和Resume方法来同步线程的活动。暂停它时,您无法知道线程正在执行的代码。如果在安全权限评估期间挂起线程时挂起线程,则可能会阻止AppDomain中的其他线程。如果在执行类构造函数时挂起线程,则会阻止AppDomain中尝试使用该类的其他线程。死锁很容易发生。

子循环可以工作,但并不完美

这不是最佳选择,但您可以使用与the one described in that question you linked类似的技巧。

当按下停止按钮时,不要退出循环,而是在暂停时进入并在子循环内等待。在该子循环中执行Thread.Sleep以防止CPU挂起。

这不是最有效的代码,因为它可以保持线程运行,并在恢复时再挂起100ms。

public class YourForm : Form
{
  private volatile bool _pause = false;

  private void StartButton_Click(object sender, EventArgs e)
  {
    var thread = new Thread(
      () =>
      {
        while (...)
        {
          // Periodically poll the _pause flag.
          while (_pause)
          {
            // Now that we're paused, wait until we're unpaused
            // before proceeding further in the outer loop
            Thread.Sleep(100);
          }

          // Todo: The rest of the processing here
        }
      });
    thread.Start();
  }

  private void PauseButton_Click(object sender, EventArgs e)
  {
    _pause = !_pause; // Toggle
  }
}

使用线程同步

最好的选择是使用各种线程同步结构like ManualResetEvent之一。暂停线程正是它们的设计目标。它们非常有效,因为它们是implemented with a mutex

public class YourForm : Form
{
  private volatile bool _pause = false;
  private static ManualResetEvent mre = new ManualResetEvent(true);

  private void StartButton_Click(object sender, EventArgs e)
  {
    var thread = new Thread(ThreadImplementation);
    thread.Start();
  }

  private void PauseButton_Click(object sender, EventArgs e)
  {
    _pause = !_pause;

    if(_pause)
    {
      mre.Reset();
    }
    else
    {
      mre.Set();
    }
  }

  private void ThreadImplementation()
  {
    while (...)
    {
      // Periodically wait on the event
      mre.WaitOne();

      // Todo: The rest of the processing here
    }
  }
}

答案 1 :(得分:1)

您的ManualResetEvent想法完全正确。

在每个步骤之间的事件中创建子线程WaitOne() 要暂停请求,请致电Reset();要取消暂停,请致电Set()

如果设置了该事件,WaitOne()将立即返回。

这比重复睡眠更有效率。

我怀疑ManualResetEventSlim稍快一点。