【问题标题】:How to wait for a thread to finish its work如何等待线程完成其工作
【发布时间】:2009-01-09 07:37:09
【问题描述】:

我有一个控制台应用程序。一个类(比如说 Worker)在一个单独的线程中做一些工作,并在它完成时抛出一个事件。但这永远不会发生,因为执行会立即结束。如何等待线程完成并在事件抛出后处理?

static void Main(string[] args)
{
    Worker worker = new Worker();
    worker.WorkCompleted += PostProcess;
    worker.DoWork();
}

static void PostProcess(object sender, EventArgs e) { // Cannot see this happening }

编辑:更正了语句的顺序,但这不是问题。

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

    您有一个竞赛条件,即工作可以在您注册活动之前完成。为避免竞争条件,请更改代码顺序,以便在开始工作之前注册事件,然后无论它完成多快,它都会被引发:

    static void Main(string[] args)
    {
        Worker worker = new Worker();
        worker.WorkCompleted += PostProcess;
        worker.DoWork();
    }
    

    编辑:

    好的,问题已被修改,所以看起来您真正要问的是如何等待PostProcess 完成执行。有几种方法可以做到这一点,但您必须添加更多代码。

    最简单的方法是,因为事件总是在引发它们的同一线程上执行,所以在 Worker 类创建的线程上调用 Thread.Join,例如假设线程作为属性公开:

    worker.Thread.Join();
    

    (虽然说实话,我可能会保留 Thread 私有并在调用它的 Worker 类上公开一个名为 WaitForCompletion 之类的方法)。

    替代方法是:

    1. 当它完成所有工作时,在Worker 类中有一个WaitHandle,可能是ManualResetEvent,它是Set,然后调用WaitOne

      李>
    2. Worker 类中有一个volatile bool complete 字段并在等待将其设置为true 时循环,在循环主体中使用Thread.Sleep(这可能不是一个好的解决方案,但这是可行的)。

    可能还有其他选择,但这是常见的。

    【讨论】:

    • 在这里我看到有一个内置的类叫 Worker,但我在 .net 4.0 中没有找到叫 Worker 的类。哪个版本有 Worker 类?请告诉我。
    • @Thomas - Worker 类只是 OP 使用的类的名称。我认为这是他们框架的一部分。
    【解决方案2】:

    使用 WaitHandle 类成员(WaitOne、WaitAny 或 WaitAll)

    Details Here In MSDN

    【讨论】:

      【解决方案3】:

      您是否尝试过切换语句的顺序?

      static void Main(string[] args)
      {
          Worker worker = new Worker();
          worker.WorkCompleted += PostProcess;
          worker.DoWork();
      }
      

      WorkCompleted 是一个事件处理程序,必须预先设置。这不会被分配工作人员调用。WorkCompleted += PostProcess;

      【讨论】:

      • 谢谢,但在我的真实代码中是正确的,这不是问题。
      【解决方案4】:

      Worker 类应该有一个允许客户端等待线程完成的方法。该方法将调用Thread.Join() 的适当重载来实现等待。如果没有(并且您无法修改 Worker 类),它可能有一些方法可以访问内部 Thread 对象,您可以对该对象执行 Join()。

      如果两者都没有,您需要查看(和/或在此处发布)Worker 类接口的更多详细信息,以查看它是否有适当的方法来等待完成。如果它没有,那么您将需要自己的 EventWaitHandle 对象,PostProcess() 事件处理程序可以在它被调用时发出信号。

      【讨论】:

      • Worker 类没有 Join 方法。它应该做什么?
      • 抱歉 - 我的回答好像工作对象是 Thread 类的对象。我已经修改了我的答案,使其更加通用。
      【解决方案5】:

      假设您无权访问 Worker 类,只需添加一个标志,指示 PostProcessing 何时完成并休眠直到设置该标志:

      static bool isProcessed = false;
      static void Main(string[] args)
      {
          Worker worker = new Worker();
          worker.WorkCompleted += PostProcess;
          worker.DoWork();
          while(!isProcessed)
          {
            System.Threading.Thread.Sleep(-1);
          }
      }
      
      static void PostProcess(object sender, EventArgs e) 
      { 
         // Cannot see this happening 
         isProcessed=true;
      }
      

      这应该可以解决问题,但如果没有关于您的设置的更多信息,我不能保证它是可靠的。

      【讨论】:

      • 那是手动代码和浪费CPU等待处理完成。 WaitHandles 旨在解决这些情况。
      • 我实际上不知道 WaitHandle 仅从阻塞线程发出信号时的行为是什么,我懒得测试它。我假设 PostProcess 在被阻塞的线程上执行。如果没有更多信息,这是一个可行的简单解决方案。
      【解决方案6】:

      您可以为此使用 .net 框架中的 BackgroundWorker 类。

      它完全符合您的要求。此外,它还可以处理调用自身,因此您不会为此感到痛苦。

      static void Main(string[] args)
      {
          BackgroundWorker worker = new BackgroundWorker();
      
          worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
          worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
          worker.WorkerReportsProgress = true;
          worker.WorkerSupportsCancellation = false;
          worker.DoWork += new DoWorkEventHandler(MyThreadMethod);
          worker.RunWorkerAsync();
          Console.Read();
      }
      
      static void MyThreadMethod(object sender, DoWorkEventArgs e)
      {
          Console.WriteLine("Worker starts working.");
          for (int i = 1; i <= 100; i++)
          {
              ((BackgroundWorker)sender).ReportProgress(i);
          }
          Console.WriteLine("Worker works fine.");
      }
      
      static void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
          Console.WriteLine("Worker has finished.");
      }
      
      static void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
      {
          Console.WriteLine("Worker reports progress {0}", e.ProgressPercentage);
      }
      

      【讨论】:

        【解决方案7】:

        我认为 应用程序运行 初始化线程后。并在完成通话时 应用程序.退出

        【讨论】:

          【解决方案8】:

          这听起来像是在您的工作人员中使用 Begin/End 异步模式的好选择。因此,Worker 将包含一个同步的 Work() 方法,该方法会返回工作的结果,而不只是一个开始异步执行工作的 DoWork() 方法和一个在工作完成时触发的事件,并且BeginWork()EndWork() 对,可用于异步使用。

          有关更多信息,请参阅http://msdn.microsoft.com/en-us/library/seta58yd(VS.71).aspx。可能适合您的情况?

          【讨论】:

          • (当然,假设您实际编写或以其他方式可以访问 Worker 类的源代码。)
          猜你喜欢
          • 2017-12-16
          • 1970-01-01
          • 2020-09-21
          • 1970-01-01
          • 2010-12-07
          • 1970-01-01
          • 2010-12-26
          • 1970-01-01
          相关资源
          最近更新 更多