除了通过调用thread.Join()之外,我怎么能等待一个线程完成运行?
我正在开发一个项目,其中从库中可用的应用程序调用方法。
我在按钮点击事件中有以下代码:
Thread thread = new Thread(new ThreadStart(AddPics)); thread.Priority = ThreadPriority.Highest; thread.Start(); execute();
但是在运行示例时,执行点总是会移动到执行。 如何使程序首先执行AddPics
方法。 我已经尝试过thread.Join()
并且它有效,但我想知道是否还有其他方法可以实现这一点。
您可以使用Join
或AutoResetEvent
等到添加图片线程完成, 但您不应该使用后面的答案解释。
使用AutoResetEvent
替代Join
可以获得相同的结果; 它允许您设置阻塞通知,因此在调用execute()
方法之前阻塞,并且仅在AddPics
线程完成其作业时继续:
private AutoResetEvent _finishAddPicsNotifier = new AutoResetEvent(false); private void OnMyButton_Click(object sender, EventArgs e) { //..... new Thread(AddPics) { Priority = ThreadPriority.Highest, IsBackground = true, //will explain later }.Start(); _finishAddPicsNotifier.WaitOne();//this will block until receive a signal to proceed execute();//this method will only be called after the AddPics method finished /..... } private void AddPics() { try{ //....... } finally{ _finishAddPicsNotifier.Set();//when finished adding the pictures, allow the waiting method to proceed } }
注意:设置IsBackground = true
表示该线程是后台线程,因此不会阻止应用程序终止。 在这里阅读更多关于后台线程
问题:
- 由于您使用按钮单击以运行该线程,您应该添加一些机制以防止多次单击同时多次运行代码。
- 您还在调用
Join()
或_finishAddPicsNotifier.WaitOne()
时阻止UI线程。
要解决所有这些问题 ,一个好的设计是定义OnFinishAddingPictures
方法并在完成添加图像时调用它,在这个新方法调用execute()
,你还应该从按钮点击中删除之前的execute()
调用:
private readonly object _oneAddPicturesLocker = new object(); private void OnMyButton_Click(object sender, EventArgs e) { //..... new Thread(AddPics) { Priority = ThreadPriority.Highest, IsBackground = true, }.Start(); } private void AddPics() { if (Monitor.TryEnter(_oneAddPicturesLocker)) { //we only can proceed if no other AddPics are running. try { //..... OnFinishAddingPictures(); } finally { Monitor.Exit(_oneAddPicturesLocker); } } } private void OnFinishAddingPictures() { execute(); }
注意:
- 我们使用这种机制摆脱阻塞。
- 我们确信
AddPics
只执行一次对AddPics
调用。 - 每当从非线程的线程访问表单或控件方法和属性时,请务必检查
InvokeRequired
并使用control.Invoke(/*..*/)
。
我假设您的主要观点是在使用AddPics
的线程运行时具有响应式UI? 如果是这样,那么您可以将此构造用于WinForms:
while (thread.IsAlive) System.Windows.Forms.Application.DoEvents();