gpt4 book ai didi

c# - 是否应该等待嵌套的可等待操作?

转载 作者:太空狗 更新时间:2023-10-29 18:20:01 24 4
gpt4 key购买 nike

我一直在关注this question我理解 Peter Duniho 的流行(尽管尚未被接受)答案背后的原因.具体来说,我知道等待后续长时间运行的操作会阻塞 UI 线程:

The second example does not yield during the asynchronous operation. Instead, by getting the value of the content.Result property, you force the current thread to wait until the asynchronous operation has completed.

为了我自己的利益,我什至已经确认了这一点,就像这样:

private async void button1_Click(object sender, EventArgs e)
{
var value1 = await Task.Run(async () =>
{
await Task.Delay(5000);
return "Hello";
});

//NOTE: this one is not awaited...
var value2 = Task.Run(async () =>
{
await Task.Delay(5000);
return value1.Substring(0, 3);
});

System.Diagnostics.Debug.Print(value2.Result); //thus, UI freezes here after 5000 ms.
}

但现在我想知道:您是否需要 await 嵌套在最外层可等待操作中的所有“可等待”操作?例如,我可以这样做:

private async void button1_Click(object sender, EventArgs e)
{
var value0 = await Task.Run(() =>
{
var value1 = new Func<Task<string>>(async () =>
{
await Task.Delay(5000);
return "hello";
}).Invoke();

var value2 = new Func<string, Task<string>>(async (string x) =>
{
await Task.Delay(5000);
return x.Substring(0, 3);
}).Invoke(value1.Result);

return value2;
});

System.Diagnostics.Debug.Print(value0);
}

或者我可以这样做:

private async void button1_Click(object sender, EventArgs e)
{
//This time the lambda is async...
var value0 = await Task.Run(async () =>
{
//we're awaiting here now...
var value1 = await new Func<Task<string>>(async () =>
{
await Task.Delay(5000);
return "hello";
}).Invoke();

//and we're awaiting here now, too...
var value2 = await new Func<string, Task<string>>(async (string x) =>
{
await Task.Delay(5000);
return x.Substring(0, 3);
}).Invoke(value1);

return value2;
});

System.Diagnostics.Debug.Print(value0);
}

而且它们都不会卡住 UI。哪个更好?

最佳答案

最后一个比较好(虽然比较乱)

在 TAP(基于任务的异步模式)中,任务(和其他可等待对象)表示异步操作。您基本上有 3 个选项来处理这些任务:

  • 同步等待 (DoAsync().Result, DoAsync().Wait()) - 在任务完成之前阻塞调用线程。使您的应用程序更加浪费,可扩展性更差,响应更慢并且容易出现死锁。
  • 异步等待 (await DoAsync()) - 不阻塞调用线程。它基本上将 await 之后的工作注册为等待任务完成后要执行的延续。
  • 根本不等待 (DoAsync()) - 不阻塞调用线程,也不等待操作完成。您不知道在处理 DoAsync 时抛出的任何异常

Specifically, I am aware that not awaiting a subsequent long-running operation will block the UI thread

所以,不完全是。如果你根本不等待,什么都不会阻塞,但你无法知道操作何时或是否成功完成。但是,如果您同步等待,您将阻塞调用线程,并且如果您阻塞 UI 线程,则可能会出现死锁。

结论:您应该尽可能地await您的可等待对象(例如,它不在Main 中)。这包括“嵌套的 async-await 操作”。

关于您的具体示例:Task.Run 用于将 CPU 密集型工作卸载到 ThreadPool 线程,这似乎不是您想要的模仿。如果我们使用 Task.Delay 来表示真正的异步操作(通常是 I/O 绑定(bind)),我们可以在没有 Task.Run 的情况下使用“嵌套的 async-await:

private async void button1_Click(object sender, EventArgs e)
{
var response = await SendAsync();
Debug.WriteLine(response);
}

async Task<Response> SendAsync()
{
await SendRequestAsync(new Request());
var response = await RecieveResponseAsync();
return response;
}

async Task SendRequestAsync(Request request)
{
await Task.Delay(1000); // actual I/O operation
}

async Task<Response> RecieveResponseAsync()
{
await Task.Delay(1000); // actual I/O operation
return null;
}

您可以使用匿名委托(delegate)而不是方法,但是当您需要定义类型并自己调用它们时,这会很不方便。

如果您确实需要将该操作卸载到 ThreadPool 线程,只需添加 Task.Run:

private async void button1_Click(object sender, EventArgs e)
{
var response = await Task.Run(() => SendAsync());
Debug.WriteLine(response);
}

关于c# - 是否应该等待嵌套的可等待操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28221385/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com