如何解决C#异步函数-是否等待立即在新线程上启动任务?
我正在重构一些C#代码以进行异步操作,但是恐怕我无法深入了解C#等待指令的状态。我有一种方法可能会进行一些冗长的处理,并且需要连续运行200次:
public LanDeviceInfo GetLanDBData(LanDeviceInfo device)
然后我创建一个使用它的异步版本:
public async Task<LanDeviceInfo> GetLanDBDataAsync(LanDeviceInfo device)
{
var deviceInfo = await Task.Run(() => GetLanDBData(device));
return deviceInfo;
}
然后,在用户界面上,单击按钮,就在循环内使用await关键字运行它:
private async void GenerateCommissioningFile()
{
foreach (VacFwPLCInfo thisPLC in filteredplCList)
{
try
{
plcCount++;
buttonGenerateComFile.Text = $"LanDB ({plcCount}/{filteredplCList.Count})";
await lanDB.GetLanDBDataAsync(thisPLC);
}
catch (Exception ex)
{
thisPLC.Error = true;
}
}
}
所有这些都可以正常工作,该函数以异步方式被调用并且我的UI不会被阻塞。
现在,我不明白的是为什么如下定义GetLanDBDataAsync函数可以很好地编译但不能正常工作并阻塞UI线程:
public async Task<LanDeviceInfo> GetLanDBDataAsync(LanDeviceInfo device)
{
return GetLanDBData(device);
}
据我了解,这也应该起作用。使用带有Task返回类型的async修饰符定义此函数将使编译器自动生成一个任务,只要调用GetLanDBDataAsync()
就会返回该任务。然后从await GetLanDBDataAsync()
调用GenerateCommissioningFile()
将自动使其在新线程中运行,而不会阻塞UI。当在UI线程上运行的GetLanDBData()
已经在等待异步功能时,为什么必须手动创建一个任务来运行GetLanDBDataAsync()
并在GenerateCommissioningFile()
中等待它?我觉得我真的很想念这里;)
谢谢!
解决方法
此功能:
public async Task<LanDeviceInfo> GetLanDBDataAsync(LanDeviceInfo device)
{
return GetLanDBData(device);
}
编译器应发出警告:“此异步方法缺少'await'运算符,将同步运行。编译器会将这种方法转换为如下形式:
public Task<LanDeviceInfo> GetLanDBDataAsync(LanDeviceInfo device)
{
var result = GetLanDBData(device);
return Task.FromResult(result);
}
因此,确实编译器生成了一个任务并返回了该任务,但不是您期望的那样。整个方法在调用方(UI)线程上同步运行,因此以与GetLanDBData
相同的方式对其进行阻止。
任务基本上代表了一些正在进行的工作(或什至已经完成,如上面的Task.FromResult
所示),能够检查所述工作的状态,并在工作完成(或失败)时得到通知。不必与线程有任何关系。
await someTask
的含义很粗糙-如果someTask
尚未完成,请在someTask
实际上完成后再执行其余方法。它不会启动任何新任务,也不会创建任何新线程。
您的工作版本:
public async Task<LanDeviceInfo> GetLanDBDataAsync(LanDeviceInfo device)
{
var deviceInfo = await Task.Run(() => GetLanDBData(device));
return deviceInfo;
}
非常卑鄙--
-
在
GetLanDBDataAsync
方法内创建一个代表整个操作的任务。我们将其命名为taskResult
。 -
要在线程池上执行的队列
GetLanDBData
(因为Task.Run
的文档说的就是那样,而不仅仅是因为“这是一项任务”)。从Task.Run
返回的任务代表此挂起的操作。 -
现在,如果从
Task.Run
返回的任务尚未完成(尚未完成),请将我们的taskResult
(代表整个操作)返回给调用者。 -
一段时间之后,
Task.Run
返回的任务完成-我们将执行其余代码。在这种情况下,Task.Run
的结果只会转发到我们的taskResult
,因为其余的代码就是return deviceInfo
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。