并发执行多个任务

本节内容参考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重启,所以测试开发阶段建议用少量数据。


本页地址:http://www.foxtable.com/webhelp/topics/7208.htm