关于C#:如何以及何时使用”async”和”await”

How and when to use ‘async’ and ‘await’

据我所知,asyncawait所做的主要工作之一是使代码易于编写和读取,但使用它们是否等于生成后台线程来执行长时间逻辑?

我目前正在尝试最基本的例子。我已经在网上添加了一些评论。你能为我澄清一下吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 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;
}


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 longRunningTask = LongRunningOperationAsync();开始执行LongRunningOperation

  • 我们假设主线程(线程ID=1)已经到达await longRunningTask,就完成了独立的工作。

    现在,如果longRunningTask还没有完成,并且还在运行,MyMethodAsync()将返回到它的调用方法,这样主线程就不会被阻塞。完成longRunningTask后,线程池中的线程(可以是任何线程)将返回到其先前上下文中的MyMethodAsync()并继续执行(在这种情况下,将结果打印到控制台)。

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


    关于其他答案,请看一下等待(参考文献)

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

    The following Windows Forms example illustrates the use of await in an
    async method, WaitAsynchronouslyAsync. Contrast the behavior of that
    method with the behavior of WaitSynchronously. Without an await
    operator applied to a task, WaitSynchronously runs synchronously
    despite the use of the async modifier in its definition and a call to
    Thread.Sleep in its body.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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";
    }


    From my understanding one of the main things that async and await do is to make code easy to write and read.

    它们将使异步代码易于编写和读取,是的。

    Is it the same thing as spawning background threads to perform long duration logic?

    一点也不。

    // I don't understand why this method must be marked as 'async'.

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

    // This line is reached after the 5 seconds sleep from DoSomethingAsync() method. Shouldn't it be reached immediately?

    不,因为默认情况下,async方法不会在另一个线程上运行。

    // Is this executed on a background thread?

    不。

    你可能会发现我的async/await介绍很有用。官方的msdn文档也非常好(尤其是tap部分),async团队给出了一个非常好的常见问题解答。


    解释

    下面是一个高级异步/等待的快速示例。除此之外还有很多细节需要考虑。

    注:Task.Delay(1000)模拟工作1秒。我认为最好把这看作是等待外部资源的响应。因为我们的代码正在等待响应,所以系统可以将正在运行的任务设置到一边,并在完成后返回到它。同时,它还可以在那个线程上做一些其他的工作。

    在下面的示例中,第一个块正是这样做的。它立即开始所有任务(Task.Delay行),并将它们放到一边。代码将在await a行上暂停,直到完成1秒延迟后再转到下一行。由于bcde几乎与a几乎同时开始执行(由于缺乏等待),因此在这种情况下,它们应该在大致相同的时间结束。

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

    例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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);

    输出:

    1
    2
    3
    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)

    有关同步上下文的额外信息

    注意:这是事情对我来说有点模糊的地方,所以如果我有任何错误,请纠正我,我会更新答案。对这项工作有一个基本的了解是很重要的,但只要你从不使用ConfigureAwait(false),你就可以在没有成为专家的情况下获得成功,尽管我认为你可能会失去一些优化的机会。

    这其中有一个方面使得异步/等待概念更难理解。这就是在本例中,所有这些都发生在同一个线程上的事实(或者至少是在同步上下文中看起来是同一个线程的情况)。默认情况下,await将恢复运行它的原始线程的同步上下文。例如,在ASP.NET中,您有一个httpContext,当一个请求进入时,它被绑定到一个线程上。此上下文包含特定于原始HTTP请求的内容,例如具有语言、IP地址、头等内容的原始请求对象。如果在处理某个内容的过程中切换线程,则可能最终在另一个httpcontext上尝试从该对象中提取信息,这可能会造成灾难性的后果。如果你知道你不会将上下文用于任何事情,你可以选择"不关心"它。这基本上允许您的代码在一个单独的线程上运行,而不带上下文。

    你是怎么做到的?默认情况下,await a;代码实际上是假设您确实希望捕获和恢复上下文:

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

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

    1
    await a.ConfigureAwait(false);

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

    这东西令人困惑吗?该死!你能弄明白吗?可能!一旦你掌握了这些概念,那么继续阅读斯蒂芬·克利里的解释,它更倾向于对异步/等待有技术理解的人。


    在一个简单的控制台程序中显示上述说明-

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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;
        }
    }

    输出为:

    1
    2
    3
    Starting Long Running method...
    Press any key to exit...
    End Long Running method...

    因此,

  • main通过TestAsyncWaitMethods启动长时间运行的方法。它立即返回而不停止当前线程,我们立即看到"按任意键退出"消息。
  • 尽管如此,longrunningmethod仍在后台运行。完成后,threadpool中的另一个线程将获取此上下文并显示最终消息
  • 因此,没有线程被阻塞。


    我认为你在System.Threading.Thread.Sleep中选了一个不好的例子。

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

    System.Threading.Thread.Sleep不是"正在做"的事情,它只是在睡觉,因此5秒钟后就可以到达下一行了…

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


    这里有一个快速的控制台程序,可以让后面的人明白。"tasktodo"方法是您希望使其成为异步的长时间运行的方法。让它运行async是由testasync方法完成的。测试循环方法只在"tasktodo"任务中运行,并以异步方式运行它们。您可以在结果中看到这一点,因为它们在从运行到运行的过程中并没有以相同的顺序完成—它们完成后会向控制台UI线程报告。简单化,但我认为简单化的例子比更复杂的例子更能说明模式的核心:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    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-asp net-mvc-4

    In web applications that sees a large number of concurrent requests at
    start-up or has a bursty load (where concurrency increases suddenly),
    making these web service calls asynchronous will increase the
    responsiveness of your application. An asynchronous request takes the
    same amount of time to process as a synchronous request. For example,
    if a request makes a web service call that requires two seconds to
    complete, the request takes two seconds whether it is performed
    synchronously or asynchronously. However, during an asynchronous call,
    a thread is not blocked from responding to other requests while it
    waits for the first request to complete. Therefore, asynchronous
    requests prevent request queuing and thread pool growth when there are
    many concurrent requests that invoke long-running operations.


    这里的所有答案都使用task.delay()或其他内置异步函数。但我的示例不使用这些异步函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
        // 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和网络通信之类的事情。我不确定这是否已经在微软的任务框架中——但如果不是,那将是我要添加的第一件事。

    实际上,您可以在C 4.0中使用收益率实现未来的模式。如果你想知道它是如何工作的,我可以推荐这个链接做一个体面的工作:http://code.google.com/p/fracture/source/browse/trunk/squared/tasklib/。然而,如果你自己开始玩弄它,你会发现如果你想做所有的酷事情,你真的需要语言支持——这正是微软所做的。


    请参阅此fiddle https://dotnetfiddle.net/vhzdlu(并在可能的情况下进行改进),以运行一个简单的控制台应用程序,该应用程序显示同一程序中任务、task.waitall()、async和wait运算符的用法。

    这个小提琴应该可以清除您的执行周期概念。

    这是样本代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    using System;
    using System.Threading.Tasks;

    public class Program
    {
        public static void Main()
        {              
            var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met      
            Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
            Console.WriteLine("Now Waiting for Task to be Finished");      
            Task.WaitAll(a); //Now Waiting      
            Console.WriteLine("Exiting CommandLine");      
        }

        public static async Task MyMethodAsync()
        {
            Task<int> longRunningTask = LongRunningOperation();
            // independent work which doesn't need the result of LongRunningOperationAsync can be done here
            Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
            //and now we call await on the task
            int result = await longRunningTask;
            //use the result
            Console.WriteLine("Result of LongRunningOperation() is" + result);
        }

        public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation
        {
            Console.WriteLine("LongRunningOperation() Started");
            await Task.Delay(2000); // 2 second delay
            Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
            return 1;
        }  

    }

    来自输出窗口的跟踪:enter image description here


    异步/等待

    实际上,async/await是一对关键字,它们只是创建异步任务回调的语法甜头。

    以这个操作为例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
        public static void DoSomeWork()
        {
            var task = Task.Run(() =>
            {
                // [RUNS ON WORKER THREAD]

                // IS NOT bubbling up due to the different threads
                throw new Exception();
                Thread.Sleep(2000);

                return"Hello";
            });

            // This is the callback
            task.ContinueWith((t) => {
                // -> Exception is swallowed silently
                Console.WriteLine("Completed");

                // [RUNS ON WORKER THREAD]
            });
        }

    上面的代码有几个缺点。错误不会传递下去,而且很难读取。但是异步和等待来帮助我们:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
        public async static void DoSomeWork()
        {
            var result = await Task.Run(() =>
            {
                // [RUNS ON WORKER THREAD]

                // IS bubbling up
                throw new Exception();
                Thread.Sleep(2000);

                return"Hello";
            });

            // every thing below is a callback
            // (including the calling methods)

            Console.WriteLine("Completed");

        }

    等待调用必须是异步方法。这有一些优势:

    • 返回任务的结果
    • 自动创建回调
    • 检查错误并让它们在调用堆栈中冒泡(在调用堆栈中最多不等待调用)
    • 等待结果
    • 释放主线程
    • 在主线程上运行回调
    • 为任务使用线程池中的工作线程
    • 使代码易于阅读
    • 还有很多

    注意:Async和Await与异步调用一起使用,不进行这些调用。您必须为此使用任务库,如task.run()。

    下面是等待和无等待解决方案之间的比较

    这是非异步解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
        public static long DoTask()
        {
            stopWatch.Reset();
            stopWatch.Start();

            // [RUNS ON MAIN THREAD]
            var task = Task.Run(() => {
                Thread.Sleep(2000);
                // [RUNS ON WORKER THREAD]
            });
            // goes directly further
            // WITHOUT waiting until the task is finished

            // [RUNS ON MAIN THREAD]

            stopWatch.Stop();
            // 50 milliseconds
            return stopWatch.ElapsedMilliseconds;
        }

    这是异步方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
        public async static Task<long> DoAwaitTask()
        {
            stopWatch.Reset();
            stopWatch.Start();

            // [RUNS ON MAIN THREAD]

            await Task.Run(() => {
                Thread.Sleep(2000);
                // [RUNS ON WORKER THREAD]
            });
            // Waits until task is finished

            // [RUNS ON MAIN THREAD]

            stopWatch.Stop();
            // 2050 milliseconds
            return stopWatch.ElapsedMilliseconds;
        }

    实际上,您可以在不使用await关键字的情况下调用异步方法,但这意味着这里的任何异常都会在发布模式中被忽略:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        public static Stopwatch stopWatch { get; } = new Stopwatch();

        static void Main(string[] args)
        {
            Console.WriteLine("DoAwaitTask:" + DoAwaitTask().Result +" ms");
            // 2050 (2000 more because of the await)
            Console.WriteLine("DoTask:" + DoTask() +" ms");
            // 50
            Console.ReadKey();
        }

    异步和等待不适用于并行计算。它们用于不阻塞您的主线程。当涉及到ASP.NET或Windows应用程序时,由于网络调用而阻塞主线程是一件坏事。如果你这样做,你的应用程序将失去响应,甚至崩溃。

    查看MS文档了解更多示例。


    为了最快的学习……好的。

    • 了解方法执行流程(带图表):3分钟好的。

    • 问题反省(学习清酒):1分钟好的。

    • 快速完成语法:5分钟好的。

    • 分享开发者的困惑:5分钟好的。

    • 问题:快速将正常代码的实际实现更改为异步代码:2分钟好的。

    • 下一步该怎么办?好的。

    了解方法执行流程(带图表):3分钟好的。

    在这张图片中,只需关注6enter image description here好的。

    在6步骤:accessTheWebAsync()已经耗尽了它在没有getStringTask结果的情况下可以完成的工作。因此,accessthewebasync使用一个wait操作符来暂停其进程并将控制权(yield)返回给调用者。accessthewebasync向调用者返回一个任务(字符串返回值)。任务表示产生字符串结果的承诺。但是什么时候回电话呢?又打了第二个电话?好的。

    accessTheWebAsync()的调用者只做了等待(它可以完成一些内部任务,然后在需要时等待)。因此,调用方正在等待访问WebAsync,而AccessTheWebAsync正在等待GetStringAsync。好的。

    记住,方法已经返回,不能再次返回(第二次)。那么打电话的人怎么知道呢?一切都与任务有关!任务已返回。任务已等待(不是方法,不是值)。值将在任务中设置。任务状态将设置为"完成"。调用者只监视任务。以后再看。好的。

    为了学习而反省问题:1分钟好的。

    让我们稍微调整一下这个问题:好的。

    How and When to use async and await Tasks?

    Ok.

    因为学习Task会自动覆盖另外2个。至少为了学习。当然,这是你关于asyncawait的问题的答案。好的。

    快速完成语法:5分钟好的。

    • 转换前(原始方法)好的。

      internal static int Method(int arg0, int arg1)
      {
      int result = arg0 + arg1;
      IO(); // Do some long running IO.
      return result;
      }
      好的。

    • 调用上述方法的另一个任务化方法好的。

      internal static Task MethodTask(int arg0, int arg1)
      {
      Task task = new Task(() => Method(arg0, arg1));
      task.Start(); // Hot task (started task) should always be returned.
      return task;
      }
      好的。

    我们提到了等待还是异步?不,调用上面的方法,就得到一个任务。你可以监控。你已经知道任务返回了什么。整数。好的。

    • 调用任务有点棘手。让我们调用methodtask()。好的。

      internal static async Task MethodAsync(int arg0, int arg1)
      {
      int result = await HelperMethods.MethodTask(arg0, arg1);
      return result;
      }
      好的。

    我们正在等待任务完成。因此出现了await。因为我们使用了await,所以必须使用async(强制)和methodasync,前缀为'async'(编码标准)。以后在这里继续阅读好的。

    分享开发者的困惑:5分钟好的。

    开发人员犯了一个错误,没有实现Task,但它仍然有效!试着理解这个问题,并且只是这里提供的被接受的答案。希望你已经阅读并完全理解了。同样,在我们的示例中,调用一个已经构建的MethodAsync()比用自己的Task(MethodTask()实现该方法容易得多。大多数开发人员发现,在将代码转换为异步代码时,很难理解Tasks。好的。

    提示:尝试找到一个现有的异步实现(如MethodAsyncToListAsync来外包困难。所以我们只需要处理异步和等待(这很容易,和普通代码非常相似)好的。

    问题:快速将正常代码的实际实现更改为异步操作:2分钟好的。

    下面数据层中显示的代码行开始中断(很多地方)。因为我们将一些代码从.NET Framework 4.2更新到了.NET核心。我们必须在整个应用程序的1小时内解决这个问题!好的。

    1
    var myContract = query.Where(c => c.ContractID == _contractID).First();

    轻松愉快!好的。

  • EntityFramework Nuget(它具有QueryableExtensions)
  • 命名空间=Microsoft.EntityFrameworkCore
  • 代码是这样改变的好的。

    1
    var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  • 方法签名已从更改好的。

    Contract GetContract(int contractnumber)好的。

    到好的。

    async Task GetContractAsync(int contractnumber)好的。

  • 调用方法也受到影响:GetContractAsync(123456);被称为GetContractAsync(123456).Result;。好的。

  • 我们在30分钟内改变了它!好的。

  • 但是架构师告诉我们不要仅仅为了这个而使用EntityFramework库!哎呀!戏剧!然后我们做了一个定制的任务实现。你知道怎么做。仍然容易!好的。

    下一步该怎么办?在ASP.NET核心中,有一个非常好的关于将同步调用转换为异步调用的快速视频,因为这很可能是阅读本文之后的发展方向。好的。好啊。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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;
        }
    }


    更高层次:

    1)async关键字启用await,这就是它所做的一切。Async关键字不在单独的线程中运行该方法。开始的f async方法同步运行,直到它命中等待一个耗时的任务为止。

    2)您可以等待返回t类型的任务或任务的方法。您不能等待异步void方法。

    3)当主线程遇到等待耗时任务或实际工作启动时,主线程将返回到当前方法的调用方。

    4)如果主线程看到一个仍在执行的任务的等待,它不会等待它并返回到当前方法的调用方。这样,应用程序就可以保持响应。

    5)等待处理任务,现在将在线程池的单独线程上执行。

    6)当这个等待任务完成时,下面的所有代码将由单独的线程执行。

    下面是示例代码。执行并检查线程ID

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    using System;
    using System.Threading;
    using System.Threading.Tasks;

    namespace AsyncAwaitDemo
    {
        class Program
        {
            public static async void AsynchronousOperation()
            {
                Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId);
                //Task<int> _task = AsyncMethod();
                int count = await AsyncMethod();

                Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id:" + Thread.CurrentThread.ManagedThreadId);

                //int count = await _task;

                Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId);

                DependentMethod(count);

                Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId);
            }

            public static async Task<int> AsyncMethod()
            {
                Console.WriteLine("Inside AsyncMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId);
                int count = 0;

                await Task.Run(() =>
                {
                    Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id:" + Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(20000);
                    count = 10;
                });

                Console.WriteLine("Completed AsyncMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId);

                return count;
            }      

            public static void DependentMethod(int count)
            {
                Console.WriteLine("Inside DependentMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId +". Total count is" + count);
            }

            static void Main(string[] args)
            {
                Console.WriteLine("Started Main method, Thread Id:" + Thread.CurrentThread.ManagedThreadId);

                AsynchronousOperation();

                Console.WriteLine("Completed Main method, Thread Id:" + Thread.CurrentThread.ManagedThreadId);

                Console.ReadKey();
            }

        }
    }

    异步等待简单解释

    简单类比法

    一个人可以等早班火车。这就是他们所做的一切,因为这是他们目前正在执行的主要任务。(同步编程(你通常做的!))

    另一个人可能在等早班火车,一边抽烟,一边喝咖啡。(异步编程)

    什么是异步编程?

    异步编程是一个程序员选择在一个独立的线程上运行他的一些代码,而不是在执行的主线程上运行,然后在主线程完成时通知主线程。

    async关键字实际上做了什么?

    将async关键字前缀为

    1
    async void DoSomething(){ . . .

    允许程序员在调用异步任务时使用await关键字。就这样。

    为什么这很重要?

    在许多软件系统中,主线程是为专门与用户界面相关的操作而保留的。如果我在我的计算机上运行一个非常复杂的递归算法,需要5秒钟才能完成,但是我在主线程(UI线程)上运行这个算法,当用户试图单击我的应用程序上的任何内容时,它似乎被冻结,因为我的主线程已经排队,并且当前正在处理太多的操作。因此,主线程无法处理鼠标单击以从按钮单击运行方法。

    你什么时候用异步等待?

    当您做任何不涉及用户界面的事情时,最好使用异步关键字。

    所以假设你正在编写一个程序,允许用户在手机上绘制草图,但每隔5秒钟,它就会在互联网上检查天气。

    我们应该等待每5秒一次的网络投票呼叫来获取天气,因为应用程序的用户需要与移动触摸屏保持交互以绘制漂亮的图片。

    如何使用异步和等待

    下面是上面的例子,下面是一些如何编写它的伪代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
         //ASYNCHRONOUS
        //this is called every 5 seconds
        async void CheckWeather()
        {
            var weather = await GetWeather();
            //do something with the weather now you have it
        }

        async Task<WeatherResult> GetWeather()
        {

            var weatherJson = await CallToNetworkAddressToGetWeather();
            return deserializeJson<weatherJson>(weatherJson);
        }

        //SYNCHRONOUS
        //This method is called whenever the screen is pressed
        void ScreenPressed()
        {
            DrawSketchOnScreen();
        }

    据我所知,还应该在组合中添加第三个术语:Task

    Async只是一个限定符,您在方法上声明它是异步方法。

    TaskAsync函数的返回。它异步执行。

    你是一个任务。当代码执行到达这一行时,控件跳回到周围原始函数的调用方。

    相反,如果将Async函数的返回(即Task赋给变量,则当代码执行到达此行时,它将继续超出周围函数中的此行,而Task则异步执行。


    在以下代码中,httpclient方法getbytearrayasync返回一个任务getContentStack。该任务承诺在任务完成时生成实际的字节数组。wait运算符应用于getContentStack,以在sumPageSizesAsync中暂停执行,直到getContentStack完成。同时,控件返回给sumpagesizesasync的调用方。当getContentStack完成时,await表达式的计算结果为字节数组。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private async Task SumPageSizesAsync()
    {
        // To use the HttpClient type in desktop apps, you must include a using directive and add a
        // reference for the System.Net.Http namespace.
        HttpClient client = new HttpClient();
        // . . .
        Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
        byte[] urlContents = await getContentsTask;

        // Equivalently, now that you see how it works, you can write the same thing in a single line.
        //byte[] urlContents = await client.GetByteArrayAsync(url);
        // . . .
    }

    is using them equal to spawning background threads to perform long
    duration logic?

    本文mdsn:Async and Await(c)的异步编程明确地解释了这一点:

    The async and await keywords don't cause additional threads to be
    created. Async methods don't require multithreading because an async
    method doesn't run on its own thread. The method runs on the current
    synchronization context and uses time on the thread only when the
    method is active.


    这里的答案对于有关等待/异步的一般指导很有用。它们还包含一些有关如何连接等待/异步的详细信息。在使用这个设计模式之前,我想和您分享一些您应该知道的实践经验。

    术语"wait"是字面上的,因此无论您在哪个线程上调用它,都将在继续之前等待方法的结果。在前台线程上,这是一个灾难。前台线程承担了构建应用程序的负担,包括视图、视图模型、初始动画以及用这些元素引导的其他内容。所以当你等待前台线程时,你就停止了应用程序。当没有任何事情发生时,用户会等待。这提供了负面的用户体验。

    您当然可以使用各种方法等待后台线程:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

    // Notice that we do not await the following call,
    // as that would tie it to the foreground thread.
    try
    {
    Task.Run(async () => { await AnyAwaitableMethod(); });
    }
    catch
    {}

    这些备注的完整代码位于https://github.com/marcusts/xamarin-forms-烦扰。请参阅名为awaiAsyncAntpattern.sln的解决方案。

    Github站点还提供了指向有关此主题的更详细讨论的链接。


    下面是通过打开对话框读取Excel文件的代码,然后使用Async和Wait来异步运行从Excel逐行读取并绑定到网格的代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    namespace EmailBillingRates
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                lblProcessing.Text ="";
            }

            private async void btnReadExcel_Click(object sender, EventArgs e)
            {
                string filename = OpenFileDialog();

                Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
                Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
                Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
                Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
                try
                {
                    Task<int> longRunningTask = BindGrid(xlRange);
                    int result = await longRunningTask;

                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message.ToString());
                }
                finally
                {
                    //cleanup  
                   // GC.Collect();
                    //GC.WaitForPendingFinalizers();

                    //rule of thumb for releasing com objects:  
                    //  never use two dots, all COM objects must be referenced and released individually  
                    //  ex: [somthing].[something].[something] is bad  

                    //release com objects to fully kill excel process from running in the background  
                    Marshal.ReleaseComObject(xlRange);
                    Marshal.ReleaseComObject(xlWorksheet);

                    //close and release  
                    xlWorkbook.Close();
                    Marshal.ReleaseComObject(xlWorkbook);

                    //quit and release  
                    xlApp.Quit();
                    Marshal.ReleaseComObject(xlApp);
                }

            }

            private void btnSendEmail_Click(object sender, EventArgs e)
            {

            }

            private string OpenFileDialog()
            {
                string filename ="";
                OpenFileDialog fdlg = new OpenFileDialog();
                fdlg.Title ="Excel File Dialog";
                fdlg.InitialDirectory = @"c:";
                fdlg.Filter ="All files (*.*)|*.*|All files (*.*)|*.*";
                fdlg.FilterIndex = 2;
                fdlg.RestoreDirectory = true;
                if (fdlg.ShowDialog() == DialogResult.OK)
                {
                    filename = fdlg.FileName;
                }
                return filename;
            }

            private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
            {
                lblProcessing.Text ="Processing File.. Please wait";
                int rowCount = xlRange.Rows.Count;
                int colCount = xlRange.Columns.Count;

                // dt.Column = colCount;  
                dataGridView1.ColumnCount = colCount;
                dataGridView1.RowCount = rowCount;

                for (int i = 1; i <= rowCount; i++)
                {
                    for (int j = 1; j <= colCount; j++)
                    {
                        //write the value to the Grid  
                        if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                        {
                             await Task.Delay(1);
                             dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                        }

                    }
                }
                lblProcessing.Text ="";
                return 0;
            }
        }

        internal class async
        {
        }
    }