我正在为异步操作重构一些C#代码,但恐怕我没有深入了解C#等待指令的情况。我有一种方法可以进行一些可能很长的处理,并且需要连续运行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);
}
据我所知,这也应该奏效。使用任务返回类型的async修饰符定义此函数将使编译器自动生成一个任务,该任务将在调用GetLanDBDataAsync()
时返回。然后从GenerateCommissioningFile()
调用await GetLanDBDataAsync()
将自动使其在新线程中运行,而不会阻塞UI。当在UI线程上运行的GenerateCommissioningFile()
已经在等待异步函数时,为什么我必须手动创建一个任务来运行GetLanDBData()
并在GetLanDBDataAsync()
内等待它?我觉得我真的错过了什么;(
谢谢!
对于此函数:
public async Task<LanDeviceInfo> GetLanDBDataAsync(LanDeviceInfo device)
{
return GetLanDBData(device);
}
编译器应该发出警告:;这种异步方法缺少"等待"运算符,将同步运行;。编译器会将此方法转换为类似的内容:
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;
}
非常rougly意味着
在
GetLanDBDataAsync
方法中创建一个代表整个操作的任务。让我们把它命名为taskResult
。将要在线程池上执行的
GetLanDBData
排队(因为Task.Run
的文档就是这么说的,而不仅仅是因为"这是一项任务"(。从Task.Run
返回的任务表示此挂起的操作。现在,如果从
Task.Run
返回的任务尚未完成(它还没有完成(,则将我们的taskResult
(表示整个操作(返回给调用者。稍后,当
Task.Run
返回的任务完成时,我们将执行其余的代码。在这种情况下,Task.Run
的结果只是转发到我们的taskResult
,因为代码的其余部分只是return deviceInfo
。