我有一个控制台应用程序。一个类(比如说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 }
编辑:更正了语句的顺序,但这不是问题。
答案 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
的方法。
替代方法是:
在WaitHandle
班级ManualResetEvent
完成所有工作后,Worker
,可能是Set
,并致电{{1}在它上面。
在WaitOne
类中有一个volatile bool complete
字段,并在等待将其设置为Worker
时循环,在循环体中使用true
(此可能不是一个好的解决方案,但它是可行的。)
也可能有其他选择,但这是常见的选择。
答案 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)
答案 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