线程完成时通知,不锁定调用线程
我正在研究构建在.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来表示线程完成。
您可以使用自定义事件和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