是否允许.NET异步TAP方法完全是非异步的?

是否允许.NET异步TAP方法完全是非异步的?,.net,asynchronous,async-await,.net,Asynchronous,Async Await,Stephen Toub在其文章“基于任务的异步模式”中描述,实现基于任务的异步模式(TAP)的异步方法甚至可以使用特定线程(如UI线程)来执行其代码。这涉及到允许异步方法使用调用线程。在这种情况下,async方法不是真正的异步方法,它会阻塞调用线程。真的可以吗?创建一个阻止调用线程的异步方法是没有意义的,或者是没有意义的 以下是Stephen文章的相关条款: 目标环境 由TAP方法的实现来确定异步执行发生的位置。TAP方法的开发人员可以选择在线程池上执行工作负载,可以选择使用异步I/O来实现工

Stephen Toub在其文章“基于任务的异步模式”中描述,实现基于任务的异步模式(TAP)的异步方法甚至可以使用特定线程(如UI线程)来执行其代码。这涉及到允许异步方法使用调用线程。在这种情况下,async方法不是真正的异步方法,它会阻塞调用线程。真的可以吗?创建一个阻止调用线程的异步方法是没有意义的,或者是没有意义的

以下是Stephen文章的相关条款:

目标环境

由TAP方法的实现来确定异步执行发生的位置。TAP方法的开发人员可以选择在
线程池
上执行工作负载,可以选择使用异步I/O来实现工作负载,因此在大多数操作的执行中不必绑定到线程,可以根据需要选择在特定线程上运行,如UI线程,或任何数量的其他潜在上下文。甚至可能是TAP方法没有执行的情况,返回一个仅表示系统中其他地方出现的条件的
任务
(例如,表示到达排队数据结构的
TData的
任务

该方法(或其一部分)可能需要在UI线程上运行的事实并不意味着它必须阻塞,无论您是否从UI线程调用它

假设您有一个方法
Task DownloadAndShowData()
,该方法异步下载一些数据,然后将其显示在UI中。要在UI上显示下载的数据,它需要在UI线程上执行一些代码。您可以这样实现它:

async Task DownloadAndShowData()
{
    var data = await DownloadData();
    await uiSchedulerFactory.StartNew(() => ShowData(data));
}
private async Task GetInfoAndUpdateUIAsync()
{
  var info = await GetInfoAsync();
  UpdateUI(info);
}

private async Task<MyInfo> GetInfoAsync()
{
  using (var client = new HttpClient())
  {
    var httpResponse = await client.GetStringAsync(...);
    return MyInfo.Parse(httpResponse);
  }
}
这里,
uiSchedulerFactory
是一个在UI线程上执行代码的
TaskFactory
(使用
SynchrnonizationContext

在此代码中,返回的
任务
仅在使用UI线程的UI中显示数据后才能完成。但是这个方法本身并没有阻塞。如果您有如下代码:

await DownloadAndShowData();
// some more code
在UI线程上执行时,发生的情况是当到达
wait
时,当前方法被“暂停”,UI线程被释放。下载完成后,在UI线程上执行上面的
ShowData()
(它可以执行,没有任何东西阻塞线程)。完成后,调用方法为“unpused”,并执行
//更多的代码

总之,异步方法在UI线程上执行一些代码(调用代码也可能在UI线程上执行),但异步方法不会阻塞。

您可以找到我的,特别是讨论“上下文”的部分

一个常见的示例是下载一些信息,将其解析为数据结构,然后更新UI,如下所示:

async Task DownloadAndShowData()
{
    var data = await DownloadData();
    await uiSchedulerFactory.StartNew(() => ShowData(data));
}
private async Task GetInfoAndUpdateUIAsync()
{
  var info = await GetInfoAsync();
  UpdateUI(info);
}

private async Task<MyInfo> GetInfoAsync()
{
  using (var client = new HttpClient())
  {
    var httpResponse = await client.GetStringAsync(...);
    return MyInfo.Parse(httpResponse);
  }
}
现在,当HTTP响应进入并且
GetInfoAsync
继续执行时,它将继续在线程池线程而不是UI线程上执行。因此,
MyInfo.Parse
将在线程池而不是UI上执行。解析完成后,
任务
将完成,
GetInfoAndUpdateUIAsync
将继续在UI线程上执行

我们不能对
GetInfoAndUpdateUIAsync
做同样的事情,因为
UpdateUI
需要在UI线程上运行


因此这就引出了一个最佳实践:在“库”中使用
ConfigureAwait(false)
异步
方法。

调用
await
方法返回
任务
对象只需委托给该任务对象即可开始调用其任务。从该任务对象的调用者的角度来看,该任务对象可能已被调用,也可能尚未被调用。如果它已经被调用,那么它可能在
await
到达它时完成,在这种情况下,从await的角度来看,任务是同步执行的


这是一个例子。另一个是
任务
知道它需要做什么;如果它需要做的事情可以更快地同步完成,那么它将简单地同步完成。一个示例可能是从远程源接收数据。许多接收数据的方法都实现TAP,这是因为数据是由远程源发送的,并且是由您的计算机在后台接收的。调用一个方法“异步”接收一个字节可能已经有了该字节,而同步返回该字节会更容易。如果尚未收到字节,则该方法将异步运行,等待远程源发送字节。当收到该字节时,它将通过continuation或
wait
后面的代码异步通知调用者。谢谢你的回答,但我不同意。在UI线程中执行的代码怎么可能不阻止该线程?也许对什么是阻塞存在误解。我使用一个测试应用程序进行了双重检查。当异步方法将工作调度到UI线程时,UI线程肯定会被阻塞。例如,这意味着只要执行“async”方法,WPF窗口就不会对任何用户输入做出反应。请参阅以下注释中的代码。这是我的测试代码:private async void Demo(){double result=wait CalculateAsync(100000000);MessageBox.Show(“result:+result”);}private Task CalculateAsync(int number){TaskFactory uiSchedulerFactory=new TaskFactory(CancellationToken.None,TaskCreationOptions.None,TaskContinuationOptions.None,TaskScheduler.FromCurrentSynchronizationContext());返回uiSchedulerFactory.StartNew(()=>{double result=0;对于(int i=1;i在UI线程上执行的代码确实会阻止它,但只会阻止一段时间。在整个方法完成之前,线程不会被阻止。我将查看您的exa