线程完成时通知,不锁定调用线程

我正在研究构建在.NET 3.5之上的遗留应用程序。 这是一个我无法改变的约束。 我需要执行第二个线程来运行长时间运行的任务而不锁定UI。 线程完成后,我需要以某种方式执行回调。

现在我尝试了这个伪代码:

Thread _thread = new Thread(myLongRunningTask) { IsBackground = True }; _tread.Start(); // wait until it's done _thread.Join(); // execute finalizer 

第二个选项不锁定UI,如下所示:

 Thread _thread = new Thread(myLongRunningTask) { IsBackground = True }; _tread.Start(); // wait until it's done while(_thread.IsAlive) { Application.DoEvents(); Thread.Sleep(100); } // execute finalizer 

当然第二种解决方案并不好,因为它过度充电UI。 _thread完成后执行回调的正确方法是什么? 另外,我如何知道线程是否被取消或中止?

* 注意:*我无法使用BackgroundWorker,我无法使用Async库,我需要使用本机线程类。

最简单的方法是基本上添加一个回调。 您甚至可以使用多播委托的工作方式来执行此操作:

 ThreadStart starter = myLongRunningTask; starter += () => { // Do what you want in the callback }; Thread thread = new Thread(starter) { IsBackground = true }; thread.Start(); 

这非常普遍,如果线程被中止或抛出exception,则不会触发回调。 你可以将它包装在一个带有多个回调的类中,或者一个指定状态的回调(中止,引发exception等)并通过包装原始委托来处理它,在带有try / catch块的方法中调用它并执行适当的回调。

除非您采取任何特殊操作,否则回调将后台线程中执行,因此您需要使用Control.BeginInvoke (或其他)来封送回UI线程。

我完全理解你的要求,但你错过了一件至关重要的事情:你是否真的需要等待该线程的同步结束? 或者你可能只需要在检测到线程结束后执行“终结器”?

在后一种情况下,只需myLongRunningTask的调用myLongRunningTask到另一个方法中:

 void surrogateThreadRoutine() { // try{ .. mytask(); // finally { .. ..all 'finalization'.. or ie raising some Event that you'll handle elsewhere } 

并将其用作线程的例程。 这样,您就会知道最终化将在线程中发生,并且在实际作业结束之后。

但是,当然,如果您使用的是某些UI或其他调度程序,那么“终结”现在将在您的线程运行 ,而不是在您的UI或通信框架的“正常线程”上运行 。 您需要确保所有资源都在您的线程外部 – 任务被正确保护或同步,否则您可能会与其他应用程序线程发生冲突。

例如,在WinForms中,在从终结器触摸任何UI内容之前,您将需要Control.InvokeRequired(surely = true)和Control.BeginInvoke / Invoke将上下文反弹回UI线程。

例如,在WPF中,在从终结器触摸任何UI内容之前,您将需要Dispatcher.BeginInvoke ..

或者,如果您控制的任何线程都可能发生冲突,那么简单的正确lock()就足够了。 等等

尝试使用ManualRestEvent来表示线程完成。

您可以使用观察者模式,看看这里:

http://www.dofactory.com/Patterns/PatternObserver.aspx

观察者模式将允许您通知之前定义为观察者的其他对象。

您可以使用自定义事件和BeginInvoke

 public event EventHandler MyLongRunningTaskEvent; private void StartMyLongRunningTask() { MyLongRunningTaskEvent += myLongRunningTaskIsDone; Thread _thread = new Thread(myLongRunningTask) { IsBackground = true }; _thread.Start(); label.Text = "Running..."; } private void myLongRunningTaskIsDone(object sender, EventArgs arg) { label.Text = "Done!"; } private void myLongRunningTask() { try { // Do my long task... } finally { this.BeginInvoke(Foo, this, EventArgs.Empty); } } 

我检查过,它在.NET 3.5下工作

也许使用条件变量和互斥,或者像wait(),signal()这样的函数,可能是等待wait()不能无限地阻塞主线程。

在C#中,这将是:

  void Notify() { lock (syncPrimitive) { Monitor.Pulse(syncPrimitive); } } void RunLoop() { for (;;) { // do work here... lock (syncPrimitive) { Monitor.Wait(syncPrimitive); } } } 

更多关于这里: 条件变量C#/。NET

它是C#中Monitor对象的概念,您还可以使用能够设置超时的版本

 public static bool Wait( object obj, TimeSpan timeout ) 

更多内容在这里: https : //msdn.microsoft.com/en-us/library/system.threading.monitor_methods(v=vs.110).aspx