在《多线程加委托实现等待窗体(loading正在加载界面),运行超时可以取消操作》一文中使用到了多线程编程,在这里做个笔记。
我们继续使用《再谈委托——同步、异步、Lambda 表达式和内置委托》一文的示例代码为本文示例。原文中使用异步委托调用同步方法,即:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'暂时移除Button1按钮点击事件
RemoveHandler Button1.Click, AddressOf Button1_Click
'******************************************
Dim method As MethodInvoker = Sub()
'模拟长时间工作代码
For i As Integer = 0 To 100
'模拟长时间工作
Threading.Thread.Sleep(100)
'显示工作进度
Dim Actiondelegate As Action(Of Integer) = Sub(int)
Me.Label1.Text = int.ToString & "%"
End Sub
Me.Label1.Invoke(Actiondelegate, i)
Next
End Sub
method.BeginInvoke(Nothing, Nothing)
'******************************************
'工作结束后记得恢复Button1按钮点击事件
AddHandler Button1.Click, AddressOf Button1_Click
End Sub
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
现修改为线程,代码如下:
Private th As Thread
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'暂时移除Button1按钮点击事件
RemoveHandler Button1.Click, AddressOf Button1_Click
'******************************************
th = New Thread(New ThreadStart(Sub()
'模拟长时间工作代码
For i As Integer = 0 To 100
'模拟长时间工作
Threading.Thread.Sleep(100)
'显示工作进度
Dim Actiondelegate As Action(Of Integer) = Sub(int)
Me.Label1.Text = int.ToString & "%"
End Sub
Me.Label1.Invoke(Actiondelegate, i)
Next
End Sub))
th.Start()
'******************************************
'工作结束后记得恢复Button1按钮点击事件
AddHandler Button1.Click, AddressOf Button1_Click
End Sub
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
运行代码,与异步委托效果一致,尝试运行中途关闭窗体,报错了:System.ComponentModel.InvalidAsynchronousStateException:“调用方法时发生错误。 目标线程不再存在。”,如图: 这是因为Me.Label1.Invoke(Actiondelegate, i)代码需在主线程(UI线程)执行,而关闭窗体后主线程已经销毁了,但子线程th还在继续运行,所以报错。应该在关闭窗体的同时关闭子线程th。添加如下代码:
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
If th.ThreadState <> ThreadState.Stopped Then
th.Abort()
End If
End Sub
多点击几次按钮,还是会报错,可能是运行了多个子线程,但只关闭了一个,最好的办法是设置线程为后台线程,当主线程关闭时后台线程也会一同关闭,在th.Start()语句之前加入th.IsBackground = True即可。 以上是使用Lambda 表达式,你也可以使用AddressOf关联委托的方法。如下:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'暂时移除Button1按钮点击事件
RemoveHandler Button1.Click, AddressOf Button1_Click
'******************************************
th = New Thread(AddressOf aa)
th.IsBackground = True '后台线程
th.Start()
'******************************************
'工作结束后记得恢复Button1按钮点击事件
AddHandler Button1.Click, AddressOf Button1_Click
End Sub
Private Sub aa()
SyncLock lockObj
For i As Integer = 0 To 100
'模拟长时间工作
Threading.Thread.Sleep(100)
'显示工作进度
'Me.Label1.Text = i.ToString & "%"
'修改为
Dim Actiondelegate As Action(Of Integer) = New Action(Of Integer)(AddressOf bb)
Me.Label1.Invoke(Actiondelegate, i)
Next
End SyncLock
End Sub
Private Sub bb(ByVal int As Integer)
'显示工作进度
Me.Label1.Text = int.ToString & "%"
End Sub
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
可见,使用多线程编程并没有想象中的那么难,其实与使用委托类似: 首先,你需引入命名空间:Imports System.Threading 其次,定义一个线程,很简单:Dim th As Thread
然后实例化,可以使用AddressOf或Lambda 表达式。th = New Thread(AddressOf 过程名)
然后使用Start方法运行线程:th.Start()
使用ThreadState或IsAlive属性监控线程状态,采用处理措施,如使用Abort方法关闭线程:th.Abort()
以下是线程常用的命令:
命令 含义
Start 引起线程开始运行
Sleep 暂停线程一段时间。格式:Thread.Sleep(毫秒数)
Suspend 线程到达安全点时暂停线程
Abort 停止线程
Resume 重新启动暂停的线程
Join 引起当前线程等待另一个线珵结果
下表是线程常用属性:
属性 值
IsAlive 如果线程是活动的,值为True
IsBackgroud 获取或设置一个布尔值,表示某个线程是否允许是后台线程
Name 获取或设置线程的名称
Priority 获取或设置一个操作系统用于区分线程调用的优先级
ApartmentState 获取或设置特定线程使用的线程模型
ThreadState 包含描述线程状态的值 有关线程下操作窗体及线程同步请参考《再谈委托——同步、异步、Lambda 表达式和内置委托》。 更多多线程编程博文,请参考: 1、使用VB.NET开发多线程 2、VB.NET多线程 3、线程的坎坷人生 4、【VB.NET2010】多线程程序编写技术分析 5、官方帮助《托管线程处理》