[英]C# asynchronous functions - does await immediately start a task on a new thread?
我正在重构一些用于异步操作的 C# 代码,但恐怕我没有深入了解 C# await 指令发生了什么。 我有一种方法可以进行一些可能冗长的处理,并且需要连续运行 200 次:
public LanDeviceInfo GetLanDBData(LanDeviceInfo device)
我创建了一个使用它的异步版本:
public async Task<LanDeviceInfo> GetLanDBDataAsync(LanDeviceInfo device)
{
var deviceInfo = await Task.Run(() => GetLanDBData(device));
return deviceInfo;
}
而且,在 UI 上,单击一个按钮,我在循环内使用 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()
时返回。 然后从GenerateCommissioningFile()
调用await GetLanDBDataAsync()
将自动使其在新线程中运行并且不会阻塞 UI。 当在 UI 线程上运行的GenerateCommissioningFile()
已经在等待异步函数时,为什么我必须手动创建一个任务来运行GetLanDBData()
并在GetLanDBDataAsync()
等待它? 我觉得我真的在这里错过了一些东西;)
谢谢!
对于此功能:
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
相同的方式GetLanDBData
它。
Task 基本上代表一些正在进行的工作(或者甚至已经完成,如上所示的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
返回的任务表示此挂起操作。
现在,如果从Task.Run
返回的任务尚未完成(它没有完成) - 将我们的taskResult
(代表整个操作)返回给调用者。
当稍后从Task.Run
返回的任务完成时 - 我们执行其余的代码。 在这种情况下, Task.Run
结果只是转发到我们的taskResult
,因为其余的代码只是return deviceInfo
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.