请教BackgroundWorker控件ProgressChanged事件与RunWorkerCompleted事件发生顺序

ttsffgg 2016-12-26 04:37:47
一个窗体,窗体上有一个按钮,一个BackgroundWorker控件
完整代码如下

Imports System.ComponentModel
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync() '开始后台线程
End Sub

Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
BackgroundWorker1.ReportProgress(1) '后台线程委托前台线程调用ProgressChanged事件

'后台线程至此结束,结束后会委托前台线程调用RunWorkerCompleted事件,注意此委托位于ReportProgress之后
End Sub

Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
MsgBox("BackgroundWorker1_ProgressChanged 线程ID是 " & Threading.Thread.CurrentThread.ManagedThreadId.ToString())
End Sub

Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
MsgBox("BackgroundWorker1_RunWorkerCompleted 线程ID是 " & Threading.Thread.CurrentThread.ManagedThreadId.ToString())
End Sub

End Class

'问题:
'为什么明明是先由BackgroundWorker1.ReportProgress委托前台线程调用ProgressChanged事件
'再由BackgroundWorker1_DoWork的结束委托前台线程调用RunWorkerCompleted事件
'结果确是RunWorkerCompleted事件内的对话框先弹出来? 顺序颠倒的原理是什么?
...全文
376 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
ttsffgg 2016-12-28
  • 打赏
  • 举报
回复
谢谢楼上两位的解答 我去看了一下BackgroundWorker控件的源码,u011266608应该是对的.
楚狂歌 2016-12-28
  • 打赏
  • 举报
回复
引用 3 楼 overown 的回复:
[quote=引用 2 楼 u011266608 的回复:] DoWork完了就进入RunWorkerCompleted,和ProgressChanged不在一个线程, ReportProgress只是通知ProgressChanged处理,不管他有没有完成 ProgressChanged在主线程中,可以访问UI 这是我的理解
不是这样,你实际运行上述代码就可以看到RunWorkerCompleted和ProgressChanged都是运行在UI线程的[/quote] 我没说清楚,我说的是DoWork...
Tiger_Zhao 2016-12-28
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 overown 的回复:]此贴的关键问题是为什么RunWorkerCompleted中的msgbox会先于ProgressChanged中的[/Quote]
MsgBox 语句不只有一个机器指令,而是需要一长串动作。
正好在对话框显示之前,RunWorkerCompleted 来了,前面的动作(第一个 MsgBox)先“入栈暂停”,优先处理当前动作(第二个 MsgBox)。

这和下面的过程类似。
假如把 MsgBox 简化成2个动作:Step1 创建对话框、Step2 显示对话框。
那么由于中间其它事件的插入,第一个 MsgBox 的显示(Button1 Step 2)会晚于第二个(TextBox1 Step 2)。
'放1个Button、1个TextBox
Public Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Debug.Print("Button1 Begin")
Debug.Print("Button1 Step 1")
Me.TextBox1.Text = "abc"
Debug.Print("Button1 Step 2")
Debug.Print("Button1 End")
End Sub

Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
Debug.Print("TextBox1 Begin")
Debug.Print("TextBox1 Step 1")
Debug.Print("TextBox1 Step 2")
Debug.Print("TextBox1 End")
End Sub

End Class

Button1 Begin
Button1 Step 1
TextBox1 Begin
TextBox1 Step 1
TextBox1 Step 2
TextBox1 End
Button1 Step 2
Button1 End
楚狂歌 2016-12-27
  • 打赏
  • 举报
回复
DoWork完了就进入RunWorkerCompleted,和ProgressChanged不在一个线程, ReportProgress只是通知ProgressChanged处理,不管他有没有完成 ProgressChanged在主线程中,可以访问UI 这是我的理解
Tiger_Zhao 2016-12-27
  • 打赏
  • 举报
回复
MsgBox 的“阻塞”效果只对本线程有效。
而你用 BackgroundWorker 控件,它的事件是线程间通讯,不受“阻塞”影响;
在第一个 MsgBox 正在准备显示的时候,强制调用了第二个 MsgBox。

多线程应该避免用 MsgBox,用进度条或一个输出日志的文本框。
ttsffgg 2016-12-27
  • 打赏
  • 举报
回复
引用 1 楼 Tiger_Zhao 的回复:
MsgBox 的“阻塞”效果只对本线程有效。 而你用 BackgroundWorker 控件,它的事件是线程间通讯,不受“阻塞”影响; 在第一个 MsgBox 正在准备显示的时候,强制调用了第二个 MsgBox。 多线程应该避免用 MsgBox,用进度条或一个输出日志的文本框。
这里用msgbox只是为了突出问题,生产环境的正式代码不会这么用 上述代码可见两处的msgbox都是运行在UI线程中而不是BackgroundWorker 控件的后台线程 此贴的关键问题是为什么RunWorkerCompleted中的msgbox会先于ProgressChanged中的
ttsffgg 2016-12-27
  • 打赏
  • 举报
回复
引用 2 楼 u011266608 的回复:
DoWork完了就进入RunWorkerCompleted,和ProgressChanged不在一个线程, ReportProgress只是通知ProgressChanged处理,不管他有没有完成 ProgressChanged在主线程中,可以访问UI 这是我的理解
不是这样,你实际运行上述代码就可以看到RunWorkerCompleted和ProgressChanged都是运行在UI线程的

16,555

社区成员

发帖
与我相关
我的任务
社区描述
VB技术相关讨论,主要为经典vb,即VB6.0
社区管理员
  • VB.NET
  • 水哥阿乐
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧