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

当前回答

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

其他回答

使用它们是否等同于生成后台线程来执行长时间的任务 时间逻辑?

这篇文章MDSN:异步编程与async和await (c#)明确解释:

async和await关键字不会引起额外的线程 创建。异步方法不需要多线程,因为Async 方法不在自己的线程上运行。该方法在电流上运行 对象时,才在线程上使用时间 方法是活动的。

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

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

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

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

异步/等待

实际上,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");
}

Await调用必须在Async方法中。这有一些优点:

返回Task的结果 自动创建回调 检查错误并让它们在callstack中冒泡(只适用于callstack中的无等待调用) 等待结果 释放主线程 在主线程上运行回调 使用线程池中的工作线程执行任务 使代码易于阅读 还有更多

注意:Async和Await用于异步调用时不做这些。为此必须使用任务库,如Task. run()。

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

这是一个非异步解决方案:

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

这是async方法:

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关键字而调用async方法,但这意味着这里的任何异常都会在释放模式下被吞噬:

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

Async和Await并不用于并行计算。它们用于不阻塞主线程。当涉及asp.net或Windows应用程序时,由于网络调用阻塞主线程是一件糟糕的事情。如果你这样做,你的应用程序将得不到响应,甚至崩溃。

查看微软文档以获得更多的例子。

为了最快的学习..

理解方法执行流程(用图表):3分钟 问题自省(为了学习):1分钟 快速浏览语法糖:5分钟 分享开发人员的困惑:5分钟 问题:快速将正常代码的实际实现更改为 异步代码:2分钟 下一步何去何从?

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

在这张图中,只关注#6(没有其他)

在第6步,执行工作耗尽并停止。要继续,它需要getStringTask(一种函数)的结果。因此,它使用await操作符来暂停进程,并将控制权交还给调用者(我们所在的方法)。对getStringTask的实际调用是在#2前面进行的。在#2中,承诺返回一个字符串结果。但是它什么时候会返回结果呢?我们应该(#1:AccessTheWebAsync)再次进行第二次调用吗?谁得到结果,#2(调用语句)还是#6(等待语句)?

AccessTheWebAsync()的外部调用者现在也在等待。调用者等待AccessTheWebAsync,而AccessTheWebAsync正在等待GetStringAsync。有趣的是AccessTheWebAsync在等待之前做了一些工作(#4),可能是为了节省等待的时间。同样的多任务自由也适用于外部调用者(以及链中的所有调用者),这是这个“异步”东西的最大优点!你觉得这是同步的,或者是正常的,但事实并非如此。

#2和#6被分开了,所以我们有#4的优势(边等待边工作)。但我们也可以不分裂。因此,#2将是:string urlContents = await client.GetStringAsync("…");这里我们没有看到任何优势,但在链的某个地方,一个函数将被分裂,而其他函数将调用它而不分裂。这取决于你使用链中的哪个函数/类。从一个函数到另一个函数的行为变化是本主题中最令人困惑的部分。

记住,该方法已经返回(#2),它不能再次返回(没有第二次)。那么,调用者如何知道呢?这都是关于任务!任务返回。等待任务状态(不是方法,不是值)。值将在任务中设置。任务状态将被设置为完成。调用者只监视任务(#6)。所以6#是在哪里/谁得到结果的答案。进一步阅读请点击这里。

为学习而反思问题:1分钟

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

如何以及何时使用async和await任务?

因为学习任务自动涵盖了其他两个(并回答了你的问题)。

整个想法非常简单。方法可以返回任何数据类型(double, int, object等),但在这里我们只是拒绝这一点,并强制返回一个'Task'对象!但我们仍然需要返回的数据(除了void),对吗?这将在'Task'对象中的标准属性中设置,例如:'Result'属性。

快速浏览语法糖:5分钟

原始的非异步方法

int方法(int arg0, int arg1) { Int result = arg0 + arg1; IO ();//执行一些长时间运行的IO。 返回结果; }

一个全新的Task-ified方法来调用上面的方法

内部静态任务<int> MethodTask(int arg0, int arg1) { Task<int> Task = new Task<int>(() => Method(arg0, arg1)); task.Start ();// Hot task(已启动的任务)应该总是返回。 返回任务; }

我们提到await或async了吗?不。调用上面的方法,就可以得到一个可以监视的任务。您已经知道任务返回(或包含)什么。一个整数。

调用Task有点棘手,这是关键字开始出现的时候。如果有一个方法调用原始方法(非异步),那么我们需要按照下面所示编辑它。让我们调用MethodTask()

内部静态异步任务<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods。MethodTask (arg0, __arg1); 返回结果; }

与上图相同的代码:

我们正在“等待”完成任务。因此使用await(强制语法) 因为使用了await,所以必须使用async(强制语法) 以Async为前缀的MethodAsync(编码标准)

await很容易理解,但其余两个(async, async)可能不是:)。好吧,这对编译器来说应该更有意义。进一步阅读请点击这里

所以有两个部分。

创建“任务”(只有一个任务,它将是一个额外的方法) 创建使用await+async调用任务的语法糖(如果要转换非异步方法,则需要更改现有代码)

记住,我们有一个外部调用AccessTheWebAsync()和调用者也没有幸免…也就是说,它也需要相同的await+async。这个链条还在继续(因此这是一个突破性的变化,可能会影响许多职业)。它也可以被认为是一个非破坏性的更改,因为原始方法仍然在那里等待调用。如果您想进行破坏性更改,则更改它的访问权限(或删除并将其移动到任务中),然后类将被迫使用task -method。无论如何,在异步调用中,总是在一端有一个任务,而且只有一个。

一切都好,但是有一个开发人员对Task感到惊讶 失踪……

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

开发人员犯了一个错误,没有实现Task,但它仍然可以工作!试着理解问题和这里提供的公认答案。希望你已经阅读并完全理解。总的来说,我们可能没有看到/实现“Task”,但它在父类/关联类的某个地方实现了。同样,在我们的例子中,调用一个已经构建的MethodAsync()要比自己用Task(MethodTask())实现该方法容易得多。大多数开发人员发现在将代码转换为异步代码时很难理解任务。

提示:尝试寻找一个现有的异步实现(如MethodAsync或ToListAsync)来外包这个困难。所以我们只需要处理Async和await(这很简单,非常类似于正常的代码)

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

数据层中显示的代码行开始中断(许多地方)。因为我们从。net framework 4.2中更新了一些代码。*到。net核心。我们必须在1小时内修复整个应用程序!

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

easypeasy !

我们安装了EntityFramework nuget包,因为它有QueryableExtensions。或者换句话说,它执行异步实现(任务),所以我们可以在代码中使用简单的Async和等待。 namespace =微软。EntityFrameworkCore

呼叫代码行是这样改变的

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

方法签名从

GetContract(int contractnumber)

to

async Task<Contract> GetContractAsync(int contractnumber)

调用方法也受到影响:GetContract(123456);GetContractAsync(123456).Result;

等等!结果是什么?好赶上!GetContractAsync只返回一个任务而不是我们想要的值(合约)。一旦操作的结果可用,它就会被存储,并在后续调用result属性时立即返回。 我们也可以用类似的'Wait()'来实现超时

TimeSpan ts = TimeSpan. frommilliseconds (150);

如果(!t.Wait (ts)) 控制台。WriteLine("超时间隔已过");

我们在30分钟内把它换遍了所有地方!

但是架构师告诉我们不要仅为此使用EntityFramework库!哦!戏剧!然后我们创建了一个自定义的Task实现。你知道怎么做。还容易!..还咯咯大笑。

下一步何去何从? 我们可以观看一个关于在ASP中将同步调用转换为异步调用的视频。Net Core,也许这是读了这篇文章后人们会去的方向。或者我解释得够多了吗?;)

在更高的层次上:

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

2)你可以等待一个返回Task或t类型Task的方法。你不能等待async void方法。

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

4)如果主线程看到一个仍在执行的任务在等待,它不会等待它,而是返回到当前方法的调用者。通过这种方式,应用程序保持响应性。

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

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

下面是示例代码。执行它并检查线程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();
        }

    }
}