异步函数示例
我们增加一个名为"Task"的函数,代码为:
Dim
Sum As
Integer
For
i As
Integer = 1
To 10
Sleep(20)
'暂停线程20毫秒,模拟需要耗时的任务.
Sum = Sum
+ i
Next
Return
Sum
提示: Sleep是新增加的一个方法,用于暂停当前线程,这里用于模拟需要耗时的任务。
上述函数执行一次需要时间200毫秒(10 * 20),我们在命令窗口按顺序执行此函数50次:
Dim
dt As
Date = Date.Now
For
i As
Integer = 1
To 50
Functions.Execute("Task")
Next
Dim
tp As
TimeSpan = Date.Now
- dt
Output.Show(tp.TotalSeconds)
'显示总的耗时
用时大概10秒多一点,和实际计算的时间相符(50 * 200 = 10000毫秒),在这10秒时间内,你无法执行任何操作,单击菜单或表格,都不会有响应。
我们修改一下代码,用AsyncExecute代替Execute:
Dim
dt As
Date = Date.Now
For
i As
Integer = 1
To 50
Functions.AsyncExecute("Task")
Next
Dim
tp As
TimeSpan = Date.Now
- dt
Output.Show(tp.TotalSeconds)
'显示总的耗时
由于改用了AsyncExecute调用,所以函数Task成为了异步函数
你猜猜用时是多少? 10秒?1秒?0.1秒? 还是更少呢?
以上答案都不对,实际上用时是0秒,并不是说执行50次函数不需要时间,而是这里采用了异步执行方式,每个函数都在独立的线程中运行,不会阻塞主线程,不会导致程序无响应,这就是异步执行的好处。
我们将这种采用异步方式执行的函数,称为异步函数; AsyncExecute方法并不会返回函数的执行结果,这是可以理解的,因为AsyncExecute开启一个新的线程来执行函数后,会立即返回主线程,并不会等函数执行完毕,自然无法获取函数的执行结果。
如何获取异步函数的执行结果呢,我们在下一节讲述。
异步函数的问题
首先我们在全局代码中定义一个Public变量:
Public Total As Integer
关于Public变量,参考:Public变量
首先增加一个名为"AddTotal"的函数,代码为:
For
i As
Integer = 1
To 100
Total
= Total +
1
Next
然后在命令窗口用AsyncExecute方法异步执行此函数100次:
Total
= 0
For
i As
Integer = 1
To 100
Functions.AsyncExecute("AddTotal")
Next
Sleep
(2000)
'等待2秒,待所有子线程线程执行完毕
Return
Total
你可能认为Total的最终结果是10000,可反复测试后你会发现,Total只会接近10000,而且每次执行结果都会不同。
如果你不相信,认为只是一些子线程没有执行完毕而已,那么你可以将Sleep的参数增大,延长等待时间,你会发现Total的值确实不会
等于10000。
为什么会这样? 因为主线程用AsyncExecute方法异步执行AddTotal函数100次,等于开启了100个子线程同时执行AddTotal方法;假定某个时刻Total的值为99,此时A线程取Total的值得到99,准备加1,并将结果保存回Total变量,假定A线程在将结果保存 回Total变量之前,另一个线程也取Total变量的值,也得到99,同样加1后保存回Total变量,这样两个线程执行完毕之后,Total的值是100,而不是我们期望的101。这里只是结果不正确而已,如果你采用 var变量代替public变量,那么不仅仅是结果不正确的问题,程序可能会直接崩溃。
要解决这个问题,必须单独定义一个同步函数用于累加异步函数结果,这是下一节要讲述的内容。