C# 异步函数 - await 会立即在新线程上启动任务吗?
C# asynchronous functions - does await immediately start a task on a new thread?
我正在重构一些用于异步操作的 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);
}
据我了解,这也应该有效。使用带有 Task return 类型的 async 修饰符定义此函数将使编译器自动生成一个任务,只要调用 GetLanDBDataAsync()
就会被 returned。然后从 GenerateCommissioningFile()
调用 await GetLanDBDataAsync()
会自动在新线程中使其成为 运行 而不会阻塞 UI。为什么我必须手动创建一个任务到 运行 GetLanDBData()
并在 GetLanDBDataAsync()
内等待 GenerateCommissioningFile()
,运行 在 UI 线程上,已经在等待异步函数?我觉得我真的在这里错过了什么 ;)
谢谢!
对于此函数:
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);
}
因此编译器确实生成了一个任务并 return 编辑了它,但不是以您期望的方式。整个方法 运行 在调用者 (UI) 线程上同步,因此以与 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
编辑 return 的任务表示此待处理操作。
现在,如果从 Task.Run
编辑的任务 return 尚未完成(它没有) - return 我们的 taskResult
(代表整个操作)返回给调用者。
一段时间后,从 Task.Run
编辑的任务 return 完成时 - 我们将执行其余代码。在这种情况下,Task.Run
的结果只是转发给我们的 taskResult
,因为其余代码只是 return deviceInfo
.
我正在重构一些用于异步操作的 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);
}
据我了解,这也应该有效。使用带有 Task return 类型的 async 修饰符定义此函数将使编译器自动生成一个任务,只要调用 GetLanDBDataAsync()
就会被 returned。然后从 GenerateCommissioningFile()
调用 await GetLanDBDataAsync()
会自动在新线程中使其成为 运行 而不会阻塞 UI。为什么我必须手动创建一个任务到 运行 GetLanDBData()
并在 GetLanDBDataAsync()
内等待 GenerateCommissioningFile()
,运行 在 UI 线程上,已经在等待异步函数?我觉得我真的在这里错过了什么 ;)
谢谢!
对于此函数:
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);
}
因此编译器确实生成了一个任务并 return 编辑了它,但不是以您期望的方式。整个方法 运行 在调用者 (UI) 线程上同步,因此以与 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
编辑 return 的任务表示此待处理操作。现在,如果从
Task.Run
编辑的任务 return 尚未完成(它没有) - return 我们的taskResult
(代表整个操作)返回给调用者。一段时间后,从
Task.Run
编辑的任务 return 完成时 - 我们将执行其余代码。在这种情况下,Task.Run
的结果只是转发给我们的taskResult
,因为其余代码只是return deviceInfo
.