【问题标题】:How to call a completion method everytime ThreadPool.QueueUserWorkItem method is returned每次返回 ThreadPool.QueueUserWorkItem 方法时如何调用完成方法
【发布时间】:2013-10-30 17:42:53
【问题描述】:

我正在使用

System.Threading.ThreadPool.QueueUserWorkItem(x => MyMethod(param1, param2, param3, param4, param5));

我想在每次调用 MyMethod 完成时从主线程调用以下方法:

UpdateGui()
{

}

我该怎么做?

谢谢!

【问题讨论】:

  • MyMethod 中调用Control.Invoke 以将委托的执行编组到UI 线程上。
  • 为什么不使用Task 并为此继续?
  • @BrianRasmussen:是的,甚至更好!
  • @BrianRasmussen,UpdateGui 存在我不理解的死锁问题,所以我想在主线程上运行它。使用 ContinueWith 会触发一个新任务,并且死锁问题仍然存在。我想要的只是在旋转线程方法返回后在主线程上调用一个方法。
  • @user277498:您可以指定在启动任务/继续时要使用的同步上下文。

标签: c# multithreading threadpool


【解决方案1】:

保持工作项的全局计数器排队和保护它的对象:

int runningTasks = 0;
object locker = new object();

每次添加任务时,计数器都会增加:

lock(locker) runningTasks++;
System.Threading.ThreadPool.QueueUserWorkItem(x => MyMethod(param1, param2, param3, param4, param5));

MyMethod 结束时递减计数器并向主线程发出信号:

lock(locker) 
{
    runningTasks--;
    Monitor.Pulse(locker);
}

在主线程中(假设这不是 GUI 线程!):

lock(locker)
{
    while(runningTasks > 0)
    {
        Monitor.Wait(locker);            
        UpdateGUI();
    }
}

这样你也有一个障碍来等待所有待处理的任务完成。

如果您不想等待,只需完全跳过主线程并在MyMethod 完成时调用UpdateGUI 将更新转发到GUI 线程。

注意MyMethod 中你应该有某种形式的Dispatcher.BeginInvoke (WPF) 或Control.BeginInvoke (WinForms) 否则你不能安全地更新GUI!

【讨论】:

  • 我不明白。 locker 是一个 System.Object,对吧? .Pulse 和 .Wait 是 System.Object 的扩展方法吗?而while循环周围的锁,不会永远阻塞MyMethod结束时的锁和递减吗?
  • @KristoferA-Huagati.com:不,当您在lock(locker) 内调用Monitor.Wait(locker) 时,锁会自动释放以允许其他线程获取锁。此外,PulseWait 是类 Monitor 的静态方法,而不是 object 的扩展。
【解决方案2】:

在线程池方法结束时将对 updategui 方法的调用发回 ui 线程的同步上下文...

例子:

private SynchronizationContext _syncContext = null;

public Form1()
{
    InitializeComponent();

    //get hold of the sync context
    _syncContext = SynchronizationContext.Current;
}

private void Form1_Load(object sender, EventArgs e)
{
    //queue a call to MyMethod on a threadpool thread
    ThreadPool.QueueUserWorkItem(x => MyMethod());
}

private void MyMethod()
{
    //do work...

    //before exiting, call UpdateGui on the gui thread
    _syncContext.Post(
        new SendOrPostCallback(
            delegate(object state)
            {
                UpdateGui();
            }), null);
}

private void UpdateGui()
{
    MessageBox.Show("hello from the GUI thread");
}

【讨论】:

    【解决方案3】:

    假设MyMethod是同步方法,在QueueUserWorkItem内部调用以使其异步执行,可以使用以下方法:

    ThreadPool.QueueUserWorkItem(x => 
    {
        MyMethod(param1, param2, param3, param4, param5);
        UpdateGui();
    });
    

    注意,您必须通过调用 Invoke/BeginInvoke 来更新 UpdateGui() 中的 GUI 元素。

    【讨论】:

      【解决方案4】:

      这可以让客户端更干净,让类处理跨线程切换机制。这样,GUI 就会以正常方式使用您的类。

      public partial class Form1 : Form
      {
          private ExampleController.MyController controller;
          public Form1()
          {          
              InitializeComponent();
              controller = new ExampleController.MyController((ISynchronizeInvoke) this);
              controller.Finished += controller_Finished;
      
          }
          void controller_Finished(string returnValue)
          {
              label1.Text = returnValue;
          }
          private void button1_Click(object sender, EventArgs e)
          {
              controller.SubmitTask("Do It");
          }
      }
      

      GUI 表单订阅了类的事件,却不知道它们是覆盖线程的。

      public class MyController
      {
          private ISynchronizeInvoke _syn;
          public MyController(ISynchronizeInvoke syn) {  _syn = syn; }
          public event FinishedTasksHandler Finished;
          public void SubmitTask(string someValue)
          {
              System.Threading.ThreadPool.QueueUserWorkItem(state => submitTask(someValue));
          }
      
          private void submitTask(string someValue)
          {
              someValue = someValue + " " + DateTime.Now.ToString();
              System.Threading.Thread.Sleep(5000);
      //Finished(someValue); This causes cross threading error if called like this.
      
              if (Finished != null)
              {
                  if (_syn.InvokeRequired)
                  {
                      _syn.Invoke(Finished, new object[] { someValue });
                  }
                  else
                  {
                      Finished(someValue);
                  }
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2019-09-26
        • 2021-02-19
        • 2022-01-24
        • 2016-01-09
        • 1970-01-01
        • 1970-01-01
        • 2021-11-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多