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

示例1

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

示例2

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

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

我是否需要用任务包装同步代码。运行使方法可等待和异步,以不阻塞UI线程?

我试图弄清楚如果我的方法是一个任务或返回任务<T>我需要用任务包装代码。运行使其异步。

我在网上看到一些例子,人们在等待没有任何异步的代码,也没有包装在任务中。运行或StartNew。


当前回答

当使用async修饰一个方法时,要记住的最重要的事情之一是,在方法中至少有一个await操作符。在您的示例中,我将使用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();
}

其他回答

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

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

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

他们不是什么已经够多了;以下是它们:

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

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

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;
}

So, the basic pattern of things is to have async code depend on "awaitables" in its await expressions. These "awaitables" can be other async methods or just regular methods returning awaitables. Regular methods returning Task/Task<T> can use Task.Run to execute code on a background thread, or (more commonly) they can use TaskCompletionSource<T> or one of its shortcuts (TaskFactory.FromAsync, Task.FromResult, etc). I don't recommend wrapping an entire method in Task.Run; synchronous methods should have synchronous signatures, and it should be left up to the consumer whether it should be wrapped in a 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());
  ...
}

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

当使用async修饰一个方法时,要记住的最重要的事情之一是,在方法中至少有一个await操作符。在您的示例中,我将使用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。跑就行。