并发执行多个任务
本节内容参考CaseStudy目录下的文件"LLMCaller.Table"的窗口"并发执行"。
本节的任务是利用AI将数据表中的英文翻译成中文,如果逐行依次翻译,将是一个漫长的等待。
为了提高速度,我们可以考虑同时并发执行多个AI任务。
设计步骤
1、新增一个自定义函数TranslateAsync,用于调用DeepSeek接口完成英译中,代码为:
'''Async
Dim
dr
As
DataRow = Args(0)
'获取数据行
Dim
caller
As
New
LLMCaller("https://api.deepseek.com/chat/completions",
"deepseek-flash",
"sk-a734b83536da45e9aedc3e42605bfb3a")
'指定接入地址、模型名称和密钥
caller.SystemPrompt =
"你是英文翻译成中文的高手,输出内容只需包括翻译结果"
caller.UserMessage =
"请把这句话翻译成中文:"
& dr("名言").ToString()
If
Await
caller.GetResultAsync()
Then
dr("中文")
= caller.Result
Else
dr("中文")
= caller.Error
End
If
2、在命令窗口执行下面的代码,即可用较快的速度翻译完成整个表:
'''Async
DataTables("翻译").ReplaceFor("中文",
"")
Dim
maxCount
As
Integer
= 50
'最多允许并发50个任务
Dim
lst
As
New
List(Of
Task)
'任务集合,所有正在执行的翻译任务都加入这个集合中
For
Each
dr
As
DataRow
In
DataTables("翻译").DataRows
lst.Add(Functions.Execute("TranslateAsync",
dr))
'添加新的翻译任务到集合中
If
lst.Count >= maxCount
Then
'如果并发执行的任务数达到最大允许数
Dim
tsk =
Await
Task.WhenAny(lst)
'等候下一个任务完成
lst.Remove(tsk)
'从集合中移除已经完成的任务,为新任务腾出位置
End
If
Next
如果需要在全部翻译完成后给用户一个提示,可以在后面加上两行代码:
'''Async
DataTables("翻译").ReplaceFor("中文",
"")
Dim
maxCount
As
Integer
= 50
'最多允许并发50个任务
Dim
lst
As
New
List(Of
Task)
'任务集合,所有正在执行的翻译任务都加入这个集合中
For
Each
dr
As
DataRow
In
DataTables("翻译").DataRows
lst.Add(Functions.Execute("TranslateAsync",
dr))
'添加新的翻译任务到集合中
If
lst.Count >= maxCount
Then
'如果并发执行的任务数达到最大允许数
Dim
tsk =
Await
Task.WhenAny(lst)
'等候下一个任务完成
lst.Remove(tsk)
'从集合中移除已经完成的任务,为新任务腾出位置
End
If
Next
Await
Task.WhenAll(lst)
'等到所有任务完成
MessageBoxA.Show("已经全部翻译完成!")
Execute还是ExecuteAsync
如果你有学习前面的《异步编程》的章节,那么一定会对这一行代码有疑问:
lst.Add(Functions.Execute("TranslateAsync",
dr))
'添加新的翻译任务到集合中
TranslateAsync既然是异步函数,那么这里为什么用Execute执行,而不是用ExecuteAsync执行呢?
这是因为这个函数中有将数据写入DataTable的代码,而DataTable是不支持多线程写入的, 所以用Execute同步执行此函数,确保写入数据的代码是在主线程中完成的。
虽然是用Execute执行,但执行的效果依然是异步的感觉,因为函数中真正耗时的代码是异步执行的,就是这行:
If
Await
caller.GetResultAsync()
Then
每次执行到这里,控制权返回给主程序,所以界面不会卡死
即使用ExecuteAsync执行,极大概率也不会出错,我真的反复测试了也没有发现错误,但小心驶得万年船,今天不小心,得多年后出现问题可能查无可查。
如果你一定用ExecuteAsync,也不是不可以,参考:插入同步代码
函数执行场景推荐
| 场景 | 推荐调用方式 |
|---|---|
| 函数中的耗时操作已经 Await 异步化 | Execute(主线程执行,安全方便) |
| 函数中有大量非异步的耗时计算 | ExecuteAsync(彻底异步,避免卡死) |
| 函数需要操作 UI 或读写 DataTable | Execute(主线程执行,避免跨线程问题) |
| 函数需要操作 UI 或读写 DataTable,且有有大量非异步的耗时计算 | ExecuteAsync(彻底异步,避免卡死),通过插入同步代码操作UI或读写DataTable,避免跨线程问题 |
编程无定式,官方文档给的只是建议,遇到具体问题要灵活变通。
重要提示:
1、异步任务在后台执行,即使你关闭命令窗口(或用户窗口),已经启动的任务依然会执行。
2、即使你重新打开另一个项目,已经启动的任务依然会执行,从而导致错误提示。
3、除非关闭Foxtable,否则已经启动的任务都会继续执行完毕。
4、如果要中断执行,只能关闭Foxtable重启,所以测试开发阶段建议用少量数据。