如何以及何时使用'async'和'await'

时间:2013-01-22 09:29:59

标签: c# .net asynchronous async-await

根据我的理解,async and 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;
}

26 个答案:

答案 0 :(得分:664)

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

这是一个例子,我希望我可以解释一些正在发生的高级细节:

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

好的,所以这里发生了什么:

  1. Task<int> longRunningTask = LongRunningOperationAsync();开始执行LongRunningOperation

  2. 完成独立工作后,让我们假设主线程(线程ID = 1),然后到达await longRunningTask

    现在,如果longRunningTask尚未完成且仍在运行,MyMethodAsync()将返回其调用方法,因此主线程不会被阻止。当longRunningTask完成后,来自ThreadPool的线程(可以是任何线程)将在其先前的上下文中返回MyMethodAsync()并继续执行(在这种情况下将结果打印到控制台)。

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

答案 1 :(得分:145)

除了其他答案之外,请查看await (C# Reference)

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

  

以下Windows窗体示例说明了在await中使用await   异步方法,WaitAsynchronouslyAsync。对比那种行为   具有WaitSynchronously行为的方法。没有等待   运算符应用于任务,WaitSynchronously同步运行   尽管在其定义中使用了async修饰符并且调用了   Thread.Sleep在它的身体里。

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

答案 2 :(得分:134)

  

根据我的理解,async和await做的主要事情之一是使代码易于编写和阅读。

他们要使异步代码易于编写和阅读,是的。

  

与产生后台线程执行长持续时间逻辑是否相同?

完全没有。

  

//我不明白为什么这个方法必须标记为'async'。

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

  

//在DoSomethingAsync()方法休眠5秒后达到此行。不应该立即达成?

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

  

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

没有


您可能会发现我的async/await intro有帮助。 official MSDN docs也非常好(特别是TAP部分),async团队推出了优秀的FAQ

答案 3 :(得分:128)

说明

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

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

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

在下面的示例中,第二个块在启动后续任务之前正在启动任务并等待它完成(这是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),你就可以顺利完成它,虽然你可能会失去一些优化的机会,我认为。 / p>

这方面的一个方面使异步/等待概念有点棘手。这是事实,在这个例子中,这一切都发生在同一个线程上(或者至少看起来与其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的解释,这些解释往往更倾向于对已经异步/等待的技术理解的人。

答案 4 :(得分:52)

在简单的控制台程序中显示上述解释 -

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...

因此,

  1. Main通过TestAsyncAwaitMethods启动长时间运行的方法。这会在不停止当前线程的情况下立即返回,我们会立即看到“按任意键退出”消息
  2. 所有这些,LongRunningMethod在后台运行。完成后,来自Threadpool的另一个线程将获取此上下文并显示最终消息
  3. 因此,不会阻止线程。

答案 5 :(得分:39)

我认为您选择System.Threading.Thread.Sleep

的错误示例

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

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

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

答案 6 :(得分:18)

这是一个快速的控制台程序,可以让跟踪者明白。 “TaskToDo”方法是您想要进行异步的长时间运行方法。使其运行Async由TestAsync方法完成。测试循环方法只运行“TaskToDo”任务并运行它们Async。您可以在结果中看到它们,因为它们在运行之间没有以相同的顺序完成 - 它们在完成时向控制台UI线程报告。简单,但我认为简单的例子比更多涉及的例子更好地展示了模式的核心:

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

答案 7 :(得分:11)

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

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

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

  

在Web应用程序中看到大量并发请求   启动或具有突发性负载(并发性突然增加),   使这些Web服务调用异步将增加   您的应用程序的响应性。异步请求需要   处理同步请求的时间相同。例如,   如果请求发出需要两秒钟的Web服务调用   完成后,无论是否执行,请求都需要两秒钟   同步或异步。但是,在异步调用期间,   一个线程不会被阻止响应其他请求   等待第一个请求完成。因此,异步   请求会阻止请求排队和线程池增长   许多并发请求调用长时间运行的操作。

答案 8 :(得分:10)

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

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

答案 9 :(得分:9)

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

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

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

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

答案 10 :(得分:6)

请运行 简单控制台应用程序 ,查看此小提琴https://dotnetfiddle.net/VhZdLU(并在可能的情况下进行改进),其中显示了任务,任务.WaitAll的用法(),async和等待运算符在同一程序中。

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

以下是示例代码

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

答案 11 :(得分:6)

异步/等待

实际上Async / Await是一对关键字,它们只是用于创建异步任务回调的语法糖。

以该操作为例:

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

上面的代码有几个缺点。错误不会传递,很难读取。 但是Async和Await来帮助我们:

    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方法中。这具有一些优点:

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

注意:异步和等待与 异步调用 not 一起使用。为此,您必须使用任务库,例如Task.Run()。

这是await和none await解决方案之间的比较

这是非异步解决方案:

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

这是异步方法:

    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关键字的情况下调用异步方法,但这意味着此处的任何异常都在释放模式下被吞没:

    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 docs了解更多示例。

答案 12 :(得分:4)

用于快速学习。

  • 理解方法执行流程(带有图表):3分钟

  • 自省(学习):1分钟

  • 快速了解语法糖:5分钟

  • 分享开发人员的困惑:5分钟

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

  • 下一步要去哪里?

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

在此图像中,仅关注#6 enter image description here

在第6步:AccessTheWebAsync()耗尽了它可以执行的工作,而没有getStringTask的结果。因此,AccessTheWebAsync使用await运算符来暂停其进度并将控制权交还给调用者。 AccessTheWebAsync将Task(具有字符串返回值)返回给调用方。该任务表示产生字符串结果的承诺。 但是它将何时回电?再次打个电话?

AccessTheWebAsync()的调用者除了等待外什么都没有做(它可以完成一些内部任务,然后在需要时等待)。因此,调用方正在等待AccessTheWebAsync,而AccessTheWebAsync目前正在等待GetStringAsync。

请记住,该方法已经返回,无法再次返回(没有第二次)。那么呼叫者怎么知道?这与任务!有关。任务已返回。 等待任务(不是方法,不是值)。值将在“任务”中设置。任务状态将设置为完成。呼叫方仅监视任务。进一步阅读,供以后的here使用。

出于学习目的的内省:1分钟

让我们稍微调整一下问题:

  

如何以及何时使用 asyncawait Tasks

因为学习Task会自动覆盖其他2。至少是为了学习。当然,这是对有关asyncawait的问题的解答。

快速遍历语法糖:5分钟

  • 转换前(原始方法)

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

  • 另一个调用上述方法的Task-ified方法

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

我们提到了等待还是异步?否。调用上述方法,您将获得一个任务。您可以监视。您已经知道任务将返回什么。整数。

  • 调用任务有些棘手。让我们调用MethodTask()

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

我们正在“等待”任务完成。因此,await。由于我们使用await,因此必须使用async(mandatory)和MethodAsync,并将“ Async”作为前缀(编码标准)。进一步阅读,供以后here

使用

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

开发人员犯了一个错误,即未实现Task,但仍然可以使用!尝试理解问题,仅了解已接受的答案provided here。希望您已经阅读并完全理解。同样,在我们的示例中,调用已构建的MethodAsync()比我们自己使用TaskMethodTask())实现该方法更容易。大多数开发人员发现,在将代码转换为异步代码时,很难走开Tasks

提示:尝试查找现有的异步实现(例如MethodAsyncToListAsync)以将难度外包。因此,我们只需要处理Async和await(这很简单,与普通代码非常相似)

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

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

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

easypeasy!

  1. EntityFrameWork nuget(具有QueryableExtensions)
  2. namespace = Microsoft.EntityFrameworkCore

代码已更改为

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

    Contract GetContract(int contractnumber)

    async Task<Contract> GetContractAsync(int contractnumber)

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

  3. 我们在30分钟内到处都进行了更改!

但是架构师告诉我们不要为此使用EntityFrameWork库!哎呀!戏剧!然后,我们做了一个自定义的Task实现。你知道的。还是容易!

下一步要做什么? 我们可以观看一段精彩的快速视频,介绍Converting Synchronous Calls to Asynchronous in ASP.Net Core,因为这很可能是阅读本书后的方向。

答案 13 :(得分:4)

我想为此付出两分钱,如果任何其他答案包含我将要解释的内容,我很抱歉,我阅读了大部分内容但没有找到,但我可能错过了一些东西。

>

我看到了很多误解和很多好的解释,只是想从异步与并行编程的不同方面来解释它,我相信这会让事情更容易理解。

当您需要进行长时间计算、处理器密集型工作时,如果可能,您应该选择使用并行编程来优化内核使用。这会打开一些线程并同时处理事情。

假设您有一个数字数组,并且想要对每个数字进行一些昂贵的长计算。 Parallel 是你的朋友。

异步编程用于不同的用例。

它用于在您等待不依赖于您的处理器的东西时释放您的线程,例如 IO(向/从磁盘写入和读取),您的线程在您执行 IO 时什么都不做,当您做同样的事情时正在等待从数据库返回的昂贵查询的某些结果。

当线程等待很长时间返回结果时,异步方法会释放它。该线程可以由应用程序的其他部分使用(例如,在处理其他请求的网络应用程序中),也可以返回操作系统以供其他用途。

当您的结果完成后,同一个线程(或另一个线程)将返回给您的应用程序以继续处理。

异步编程在像 .net 这样的多线程环境中不是强制性的(但一个很好的做法),在 web 应用程序中其他线程将响应新的请求,但是如果你在像 nodejs 这样的单线程框架中它是强制性的,因为你可以不要阻塞您唯一的线程,否则您将无法响应任何其他请求。

总而言之,长时间的处理器密集型计算将从并行编程中受益更多,并且不依赖于您的处理器的长时间等待(例如 IO 或 DB 查询或对某些 API 的调用将从异步编程中受益更多。

这就是为什么实体框架,例如,有一个异步 api 来保存、列出、查找等......

请记住,async/await 与 wait 或 waitAll 不同,上下文不同。 async/await 释放线程,是异步编程。 wait / waitAll 阻塞所有线程(它们没有被释放)以在并行上下文中强制同步......不同的东西......

希望这对某人有用...

答案 14 :(得分:3)

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

答案 15 :(得分:3)

在更高级别:

1)Async关键字启用await,仅此而已。 Async关键字不会在单独的线程中运行该方法。开头的f async方法同步运行,直到它等待耗时的任务。

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

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

4)如果主线程看到正在等待执行的任务正在等待,则它不会等待,而是返回当前方法的调用方。这样,应用程序将保持响应状态。

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

6)此等待任务完成后,其下面的所有代码将由单独的线程执行

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

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

    }
}

答案 16 :(得分:3)

异步并等待简单说明

简单类比

一个人可能等待参加早班。这就是他们正在做的所有事情,因为这是他们当前正在执行的主要任务。 (同步编程(您通常会做什么!))

另一个人可能在抽烟然后喝咖啡的同时等待早上的火车。 (异步编程)

什么是异步编程?

异步编程是程序员选择在与执行主线程不同的线程上运行其某些代码,然后在完成时通知主线程的方法。

异步关键字实际上有什么作用?

将async关键字前缀为

之类的方法名称
async void DoSomething(){ . . .

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

这为什么重要?

在许多软件系统中,主线程专用于与用户界面有关的操作。如果我正在计算机上运行耗时5秒的非常复杂的递归算法,但是我在主线程(UI线程)上运行此算法,则当用户尝试单击我的应用程序中的任何内容时,它似乎被冻结了。因为我的主线程已排队,并且当前正在处理太多操作。结果,主线程无法处理鼠标单击以从按钮单击运行该方法。

何时使用异步和等待?

在执行不涉及用户界面的任何操作时,最好使用异步关键字。

因此,可以说您正在编写一个程序,该程序允许用户在手机上绘制草图,但是每5秒钟它将检查一次互联网上的天气。

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

您如何使用异步和等待

在上面的示例之后,这是一些如何编写它的伪代码:

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

答案 17 :(得分:2)

我理解的方式也是,应该在第三个术语中加入Task

Async只是你在方法上说的一个限定符,它说它是一个异步方法。

Taskasync函数的返回值。它以异步方式执行。

await一项任务。当代码执行到达此行时,控件会跳回到周围原始函数的调用者。

相反,如果您将async函数的返回值(即Task)分配给变量,当代码执行到达此行时,它只是继续超过该行在周围的函数 Task异步执行。

答案 18 :(得分:1)

在下面的代码中,HttpClient方法GetByteArrayAsync返回一个Task,getContentsTask。任务是在任务完成时生成实际字节数组的承诺。在getContentsTask完成之前,await运算符应用于getContentsTask以暂停SumPageSizesAsync中的执行。同时,控制权返回给SumPageSizesAsync的调用者。当getContentsTask完成时,await表达式求值为字节数组。

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

答案 19 :(得分:1)

  

使用它们等于生成后台线程来执行很长时间   持续时间逻辑?

本文MDSN:Asynchronous Programming with async and await (C#)明确解释了这一点:

  

async和await关键字不会导致其他线程   创建。异步方法不需要多线程,因为异步   方法不会在自己的线程上运行。该方法在当前运行   同步上下文并仅在线程上使用时间   方法有效。

答案 20 :(得分:1)

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

只有当我们想要立即得到结果时,我们才应该使用 async & await。也许函数返回的结果会在下一行中使用。

Follow this blog, It is very well written in simple word

答案 21 :(得分:1)

enter image description here最好的例子在这里,享受:

答案 22 :(得分:0)

这里的答案可作为await / async的一般指导。它们还包含有关如何连接await / async的一些细节。在使用这种设计模式之前,我想与您分享一些您应该了解的实践经验。

术语&#34; await&#34;是文字的,所以你调用它的任何线程都会在继续之前等待方法的结果。在前景主题中,这是 灾难 。前台线程承担构建应用程序的负担,包括视图,视图模型,初始动画以及其他任何与这些元素一起引导的东西。因此,当您等待前台线程时, 停止 应用程序。当没有任何事情发生时,用户等待并等待。这提供了负面的用户体验。

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

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-annoyances。请参阅名为AwaitAsyncAntipattern.sln的解决方案。

GitHub网站还提供了有关此主题的更详细讨论的链接。

答案 23 :(得分:0)

下面是通过打开对话框读取excel文件的代码然后使用async并等待运行异步从excel逐行读取并绑定到网格的代码

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

答案 24 :(得分:0)

以下是我用来决定何时使用promise和何时使用async await的经验法则

  1. 异步函数返回一个Promise。反之亦然。每个返回promise的函数都可以视为异步函数 await用于调用异步函数并等待其解析或拒绝。

  2. await阻止代码所在的异步函数中的代码执行。 如果function2的输出依赖于function1的输出,那么我使用await。

  3. 如果两个函数可以并行运行,请创建两个不同的异步函数,然后并行运行它们。

  4. 要并行运行promise,请创建一个promise数组,然后使用Promise.all(promisesArray) 每次使用await时,请记住您正在编写阻塞代码。随着时间的流逝,我们倾向于忽略这一点。

  5. 与其创建包含许多等待asyncFunction()的巨大异步函数,不如创建较小的异步函数。这样,我们将意识到不会编写太多阻塞代码。 使用较小的异步函数的另一个优点是,您可以强迫自己考虑可以并行运行的异步函数。

  6. 如果您的代码包含阻塞代码,则最好使其成为异步函数。通过这样做,您可以确保其他人可以异步使用您的函数。

  7. 通过使用阻塞代码制作异步功能,您可以使将调用您的函数的用户确定所需的异步程度。

希望这可以帮助您轻松决定何时使用Promise,何时使用Promise和何时使用async-await

答案 25 :(得分:0)

回答您的第二个问题 - 何时使用 async - 这是我们使用的一种相当简单的方法:

  • 运行时间超过 50 毫秒的长时间运行的 I/O 绑定任务 - 使用 async
  • 长时间运行的 CPU 密集型任务 - 使用并行执行、线程等。

说明:当您进行 I/O 工作时——发送网络请求、从磁盘读取数据等——实际工作由“外部”硅(网卡、磁盘控制器等)完成。一旦工作完成 - I/O 设备驱动程序将“ping”回操作系统,并且操作系统将执行您的继续代码、回调/等。在那之前,CPU 可以自由地做它自己的工作(并且作为奖励,您还可以释放一个线程池线程,这对于 Web 应用程序的可扩展性来说是一个非常好的奖励)

附言50ms 阈值是 MS 的建议。否则 async 增加的开销(创建状态机、执行上下文等)会耗尽所有好处。现在找不到原来的 MS 文章,但这里也提到了https://www.red-gate.com/simple-talk/dotnet/net-framework/the-overhead-of-asyncawait-in-net-4-5/