你必须把任务。运行在一个方法,使它异步?

我试图理解异步等待在最简单的形式。为了这个例子,我想创建一个非常简单的方法,将两个数字相加,当然,这根本不需要处理时间,这只是一个制定例子的问题。

示例1

private async Task DoWork1Async()
{
int result = 1 + 2;
}

示例2

private async Task DoWork2Async()
{
Task.Run( () =>
{
int result = 1 + 2;
});
}

如果我等待DoWork1Async()代码将同步或异步运行?

我需要包装同步代码与Task.Run,使方法可等待和异步,以不阻塞UI线程?

我试图弄清楚如果我的方法是Task或返回Task<T>,我需要用Task.Run包装代码以使其异步。

我在网上看到一些例子,人们在等待没有任何异步的代码,也没有包装在Task.RunStartNew中。

397686 次浏览

首先,让我们澄清一些术语:“异步”(async)意味着它可以在启动之前将控制权交还给调用线程。在async方法中,这些“yield”点是await表达式。

这与术语“异步”非常不同,多年来,MSDN文档使用(mis)来表示“在后台线程上执行”。

为了进一步混淆这个问题,async与“awaitable”非常不同;有一些async方法的返回类型是不可等待的,还有许多方法返回的可等待类型不是async

足够多关于他们;下面是他们:

  • async关键字允许异步方法(也就是说,它允许await表达式)。async方法可能返回TaskTask<T>或(如果必须的话)void
  • 任何遵循特定模式的类型都是可等待的。最常见的可等待类型是TaskTask<T>

因此,如果我们将你的问题重新表述为“我如何以可等待的方式运行在后台线程上操作”,答案是使用Task.Run:

private Task<int> DoWorkAsync() // No async because the method does not need await
{
return Task.Run(() =>
{
return 1 + 2;
});
}

(但这种模式是一种糟糕的方法;见下文)。

但如果你的问题是“我如何创建一个async方法,它可以返回给它的调用者而不是阻塞”,答案是声明方法async,并使用await作为它的“让步”点:

private async Task<int> GetWebPageHtmlSizeAsync()
{
var client = new HttpClient();
var html = await client.GetAsync("http://www.example.com/");
return html.Length;
}

因此,事情的基本模式是让async代码依赖于其await表达式中的“可等待对象”。这些“可等待对象”可以是其他async方法,也可以只是返回可等待对象的常规方法。返回Task/Task<T> await1的常规方法使用Task.Run在后台线程上执行代码,或者(更常见的是)它们可以使用TaskCompletionSource<T>或其快捷方式之一(TaskFactory.FromAsyncTask.FromResult等)。我await2建议将整个方法包装在Task.Run中;同步方法应该有同步签名,是否应该包装在Task.Run中应该由消费者决定:

private int DoWork()
{
return 1 + 2;
}


private void MoreSynchronousProcessing()
{
// Execute it directly (synchronously), since we are also a synchronous method.
var result = DoWork();
...
}


private async Task DoVariousThingsFromTheUIThreadAsync()
{
// I have a bunch of async work to do, and I am executed on the UI thread.
var result = await Task.Run(() => DoWork());
...
}

我的博客上有一个__ABC0 / await介绍;最后是一些很好的后续资源。async的MSDN文档也非常好。

当使用< >强异步< / >强修饰方法时,要记住的最重要的事情之一是,方法中至少有一个 < >强等待< / >强操作符。在你的例子中,我将如下所示使用TaskCompletionSource翻译它。

private Task<int> DoWorkAsync()
{
//create a task completion source
//the type of the result value must be the same
//as the type in the returning Task
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
Task.Run(() =>
{
int result = 1 + 2;
//set the result to TaskCompletionSource
tcs.SetResult(result);
});
//return the Task
return tcs.Task;
}


private async Task DoWork()
{
int result = await DoWorkAsync();
}

当你使用任务。Run运行一个方法,Task从线程池中获取一个线程来运行该方法。所以从UI线程的角度来看,它是“异步的”,因为它不会阻塞UI线程。这对于桌面应用程序来说很好,因为通常不需要很多线程来处理用户交互。

然而,对于web应用程序,每个请求都由一个线程池线程服务,因此可以通过保存这些线程来增加活动请求的数量。频繁使用线程池线程来模拟异步操作对于web应用程序来说是不可扩展的。

True Async并不需要使用线程进行I/O操作,例如文件/ DB访问等。你可以通过阅读这篇文章来理解为什么I/O操作不需要线程。http://blog.stephencleary.com/2013/11/there-is-no-thread.html

在您的简单示例中,这是一个纯cpu限制的计算,因此使用Task。跑就行。