根据我的理解,async和await所做的主要事情之一是使代码易于编写和阅读-但使用它们是否等于生成后台线程来执行长时间的逻辑?

我目前正在尝试最基本的例子。我内联添加了一些注释。你能给我解释一下吗?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}

当前回答

解释

下面是一个高层async/await的快速示例。除此之外,还有很多细节需要考虑。

注意:Task.Delay(1000)模拟工作1秒。我认为最好将此视为等待来自外部资源的响应。由于我们的代码正在等待响应,系统可以将正在运行的任务设置到一边,并在完成后返回到它。同时,它可以在该线程上做一些其他工作。

在下面的例子中,第一个块正是这样做的。它立即启动所有任务(Task。延迟线),并把它们放到一边。代码将在await一行上暂停,直到1秒的延迟完成,然后才进入下一行。由于b、c、d和e几乎与a同时开始执行(由于缺少await),因此在本例中它们应该大致同时完成。

在下面的例子中,第二个块正在启动一个任务,并在开始后续任务之前等待它完成(这就是await所做的)。每次迭代需要1秒。await是暂停程序并在继续之前等待结果。这是第一块和第二块的主要区别。

例子

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

输出:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

关于SynchronizationContext的额外信息

注意:这就是我感到有点模糊的地方,所以如果我错了什么,请纠正我,我会更新答案。对它的工作原理有一个基本的了解是很重要的,但只要你从来没有使用过ConfigureAwait(false),你也可以成为这方面的专家,尽管我认为你可能会失去一些优化的机会。

有一个方面使得异步/等待概念有点难以掌握。事实上,在这个例子中,这一切都发生在同一个线程上(或者至少在SynchronizationContext方面看起来是同一个线程)。默认情况下,await将恢复运行它的原始线程的同步上下文。例如,在ASP中。NET中你有一个HttpContext,当请求进入时它被绑定到一个线程上。此上下文包含特定于原始Http请求的内容,例如原始request对象,其中包含语言、IP地址、报头等内容。如果你在处理过程中切换线程,你可能会在不同的HttpContext中尝试从这个对象中提取信息,这可能是灾难性的。如果您知道您不会将上下文用于任何事情,您可以选择“不关心”它。这基本上允许您的代码在单独的线程上运行,而无需带上下文。

你如何做到这一点?默认情况下,await a;代码实际上做了一个假设,你想要捕获和恢复上下文:

await a; //Same as the line below
await a.ConfigureAwait(true);

如果你想让主代码在没有原始上下文的情况下继续在一个新线程上运行,你只需使用false而不是true,这样它就知道它不需要恢复上下文。

await a.ConfigureAwait(false);

在程序暂停之后,它可能会继续在一个具有不同上下文的完全不同的线程上运行。这就是性能改进的来源——它可以在任何可用的线程上继续运行,而不必恢复它开始时的原始上下文。

这些东西让人困惑吗?地狱耶!你能算出来吗?可能!一旦你掌握了概念,然后转向Stephen Cleary的解释,它往往更适合那些已经对async/await有技术理解的人。

其他回答

async与函数一起使用,使其成为异步函数。await关键字用于同步调用异步函数。await关键字保持JS引擎的执行,直到promise被解决。

我们应该只在需要立即得到结果时使用async & await。也许函数返回的结果将在下一行中使用。

关注这个博客,它用简单的文字写得很好

public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}

这里所有的答案都使用Task.Delay()或其他内置的异步函数。但下面是我的例子,没有使用这些async函数:

// Starts counting to a large number and then immediately displays message "I'm counting...". 
// Then it waits for task to finish and displays "finished, press any key".
static void asyncTest ()
{
    Console.WriteLine("Started asyncTest()");
    Task<long> task = asyncTest_count();
    Console.WriteLine("Started counting, please wait...");
    task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
    //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
    Console.WriteLine("Finished counting.");
    Console.WriteLine("Press any key to exit program.");
    Console.ReadLine();
}

static async Task<long> asyncTest_count()
{
    long k = 0;
    Console.WriteLine("Started asyncTest_count()");
    await Task.Run(() =>
    {
        long countTo = 100000000;
        int prevPercentDone = -1;
        for (long i = 0; i <= countTo; i++)
        {
            int percentDone = (int)(100 * (i / (double)countTo));
            if (percentDone != prevPercentDone)
            {
                prevPercentDone = percentDone;
                Console.Write(percentDone.ToString() + "% ");
            }

            k = i;
        }
    });
    Console.WriteLine("");
    Console.WriteLine("Finished asyncTest_count()");
    return k;
}

当使用async和await时,编译器在后台生成一个状态机。

下面是一个例子,我希望我能解释一些正在发生的高级细节:

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

好的,这里发生了什么:

Task<int> longRunningTask = LongRunningOperationAsync();开始执行LongRunningOperation 独立的工作完成了,假设主线程(线程ID = 1),然后等待longRunningTask到达。 现在,如果longRunningTask还没有完成,它仍在运行,MyMethodAsync()将返回到它的调用方法,因此主线程不会被阻塞。当longRunningTask完成时,来自ThreadPool的线程(可以是任何线程)将返回到MyMethodAsync()之前的上下文中并继续执行(在这种情况下将结果打印到控制台)。

第二种情况是longRunningTask已经完成执行,结果可用。当到达await longRunningTask时,我们已经有了结果,所以代码将继续在同一线程上执行。(在本例中将结果打印到控制台)。当然,对于上面的例子,情况并非如此,其中涉及到Task.Delay(1000)。

除了其他答案,还有await (c#参考)

更具体地说,在包含的例子中,它解释了您的情况

下面的Windows窗体示例说明了await在 异步方法,WaitAsynchronouslyAsync。对比一下它的行为 方法使用waitsynchrontically的行为。没有等待 应用到任务的操作符,waitsynchronize同步运行 尽管在定义中使用了async修饰符,并且调用了 线程。睡在它的身体里。

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}