如何以及何时使用 “异步” 和“等待”

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

答案

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

根据我的理解,异步和等待要做的主要事情之一就是使代码易于编写和阅读。

他们是为了使异步代码易于编写和阅读。

执行长时间逻辑与生成后台线程是否一样?

一点也不。

// 我不明白为什么必须将此方法标记为 “异步”。

async关键字启用await关键字。因此,任何使用await方法都必须标记为async

// 从 DoSomethingAsync()方法进入休眠状态 5 秒钟后,到达此行。不应该立即到达吗?

否,因为默认情况下async方法不在其他线程上运行。

// 这是在后台线程上执行的吗?

没有。


您可能会发现我的async / await介绍很有帮助。 MSDN 官方文档也异常出色(尤其是TAP部分),并且async团队发布了出色的FAQ

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)
await a; //Same as the line below
await a.ConfigureAwait(true);
await a.ConfigureAwait(false);
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";
}
class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}
Starting Long Running method...
Press any key to exit...
End Long Running method...

我认为您在System.Threading.Thread.Sleep选择了一个错误的示例

async任务的async是让它在后台执行而不锁定主线程,例如执行DownloadFileAsync

System.Threading.Thread.Sleep不是 “正在完成” 的东西,它只是睡眠,因此在 5 秒后到达下一行...

阅读本文,我认为这是对asyncawait概念的很好解释: http : //msdn.microsoft.com/zh-cn/library/vstudio/hh191443.aspx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}

该答案旨在提供一些特定于 ASP.NET 的信息。

通过在 MVC 控制器中使用 async / await,可以提高线程池利用率并实现更好的吞吐量,如以下文章所述,

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

在启动时看到大量并发请求或负载突增(并发性突然增加)的 Web 应用程序中,使这些 Web 服务调用异步可以提高应用程序的响应能力。异步请求与同步请求所花费的时间相同。例如,如果某个请求进行的 Web 服务调用需要两秒钟才能完成,那么无论是同步还是异步执行,该请求都将花费两秒钟。但是,在异步调用期间,在等待第一个请求完成时,不会阻止线程响应其他请求。因此,当有许多并发请求调用长时间运行的操作时,异步请求会阻止请求排队和线程池增长。

// Starts counting to a large numbewr 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;
    }

老实说,我仍然认为最好的解释是关于维基百科上关于未来和承诺的解释: http : //en.wikipedia.org/wiki/Futures_and_promises

基本思想是您有一个单独的线程池,这些线程池异步执行任务。使用时。但是,该对象确实保证会在某个时间执行该操作,并在您请求时为您提供结果。这意味着它将在您请求结果并且尚未完成时阻塞,否则将在线程池中执行。

您可以从那里进行优化:可以异步实现某些操作,还可以通过将后续请求组合在一起和 / 或重新排序来优化文件 IO 和网络通信之类的功能。我不确定这是否已经存在于 Microsoft 的任务框架中 - 但如果不是,那将是我要添加的第一批内容。

您实际上可以在 C#4.0 中使用 yields 实现将来的模式排序。如果您想知道它是如何工作的,我可以推荐这个链接,它做得不错: http : //code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ 。但是,如果您自己开始玩弄它,您会注意到,如果您想做所有很酷的事情,那么您确实需要语言支持 - 这正是 Microsoft 所做的。