如何等待线程完成其工作

时间:2009-01-09 07:37:09

标签: c# multithreading

我有一个控制台应用程序。一个类(比如说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 }

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

8 个答案:

答案 0 :(得分:15)

你有一个竞争条件,因为工作可以在你注册参加比赛之前完成。为了避免竞争条件,更改代码的顺序,以便在开始工作之前注册事件,然后无论结束有多快,它都会被引发:

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

修改

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

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

worker.Thread.Join();

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

替代方法是:

  1. WaitHandle班级ManualResetEvent完成所有工作后,Worker,可能是Set,并致电{{1}在它上面。

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

  3. 也可能有其他选择,但这是常见的选择。

答案 1 :(得分:2)

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

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

WorkCompleted是一个事件处理程序,必须预先设置。这不会被赋值worker调用.WorkCompleted + = PostProcess;

答案 2 :(得分:2)

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

请参阅Details Here In MSDN

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

假设您无权访问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;
}

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

答案 5 :(得分:0)

您可以使用.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);
}

答案 6 :(得分:0)

这听起来像是在工作者中使用Begin / End异步模式的好选择。因此,DoWork()将包含一个返回结果的同步Worker方法,而不仅仅是开始异步工作的Work()方法和工作完成时触发的事件,BeginWork()将包含一个返回结果的同步EndWork()方法。 )工作,以及可用于异步使用的{{1}}和{{1}}对。

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

答案 7 :(得分:0)

我想 Application.Run 初始化你的线程后。并在完成通话 Application.Exit