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;
}

而且,在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意味着

  1. GetLanDBDataAsync方法中创建一个代表整个操作的任务。让我们把它命名为taskResult

  2. 将要在线程池上执行的GetLanDBData排队(因为Task.Run的文档就是这么说的,而不仅仅是因为"这是一项任务"(。从Task.Run返回的任务表示此挂起的操作。

  3. 现在,如果从Task.Run返回的任务尚未完成(它还没有完成(,则将我们的taskResult(表示整个操作(返回给调用者。

  4. 稍后,当Task.Run返回的任务完成时,我们将执行其余的代码。在这种情况下,Task.Run的结果只是转发到我们的taskResult,因为代码的其余部分只是return deviceInfo

最新更新