繁体   English   中英

C# 异步函数 - 是否会立即在新线程上启动任务?

[英]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;
}

非常粗鲁的意思是——

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

  2. 将要在线程池上执行的队列GetLanDBData (因为Task.Run的文档Task.Run是这样做的,而不仅仅是因为“这是一项任务”)。 Task.Run返回的任务表示此挂起操作。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM