首页 文章

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

提问于
浏览
798

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

20 回答

  • 2

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

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

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

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

  • 9

    在更高的层次上:

    1)Async关键字启用等待,这就是它的全部功能 . Async关键字不会在单独的线程中运行该方法 . 开始的f async方法同步运行,直到它等待一个耗时的任务 .

    2)您可以等待返回类型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();
            }
    
        }
    }
    
  • 17

    解释

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

    注意: Task.Delay(1000) 模拟工作1秒钟 . 我认为's best to think of this as waiting for a response from an external resource. Since our code is waiting for a response, the system can set the running task off to the side and come back to it once it'已经完成了 . 同时,它可以在该线程上做一些其他的工作 .

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

    在下面的示例中, second block 正在启动任务并等待它完成(这是 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);
    

    OUTPUT:

    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中将信息从这个对象中提取出来,这可能是灾难性的 . 如果您知道不会使用任何上下文,则可以选择"not care" . 这基本上允许您的代码在单独的线程上运行,而不会带来上下文 .

    你是如何实现这一目标的?默认情况下, await a; 代码实际上假设您要捕获并恢复上下文:

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

    如果您希望允许主代码在没有原始上下文的情况下继续使用新线程,则只需使用false而不是true,因此它知道它不需要恢复上下文 .

    await a.ConfigureAwait(false);
    

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

    这个东西令人困惑吗?好吧,好吧!你能搞清楚吗?大概!一旦你掌握了概念,然后转向Stephen Cleary的解释,这些解释往往更倾向于对已经异步/等待有技术理解的人 .

  • 11

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

    术语"await"是文字的,所以无论你调用它什么线程都会在继续之前等待方法的结果 . 在 foreground 线程上,这是 disaster . 前台线程承担构建应用程序的负担,包括视图,视图模型,初始动画以及其他任何与这些元素一起引导的东西 . 所以当你等待前台线程时,你 stop 应用程序 . 当没有任何事情发生时,用户等待并等待 . 这提供了负面的用户体验 .

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

    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站点还提供了有关此主题的更详细讨论的链接 .

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

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

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

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

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

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

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

  • 6

    我认为你已经选择了一个错误的例子 System.Threading.Thread.Sleep

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

    System.Threading.Thread.Sleep 不是"being done",它只是睡觉,因此你的下一行在5秒后到达...

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

  • 3

    下面是通过打开对话框读取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
        {
        }
    }
    
  • 36

    请查看此小提琴https://dotnetfiddle.net/VhZdLU(并在可能的情况下进行改进)以运行 simple console application ,它在同一程序中显示 Task, Task.WaitAll(), async and await 运算符的用法 .

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

    这是示例代码

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

    Trace coming from Output Window:

  • 1

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

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

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

    一点也不 .

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

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

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

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

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

    没有 .


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

  • 46

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

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

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

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

  • 0

    我理解它的方式也是,应该在混合中增加第三个术语: Task .

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

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

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

    相反,如果将 async 函数(即 Task )的返回值分配给变量,则当代码执行到达此行时,它会在 Task 异步执行时继续超出周围函数中的那一行 .

  • 9

    异步/等待

    实际上,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方法中 . 这有一些优点:

    • 返回任务的结果

    • 自动创建回调

    • 检查错误并让它们在callstack中冒泡(最多只能等待 - 在callstack中等待调用)

    • 等待结果

    • 释放主线程

    • 在主线程上运行回调

    • 使用线程池中的工作线程来执行任务

    • 使代码易于阅读

    • 还有更多

    NOTE :Async和Await与异步调用一起使用而不是这些 . 您必须使用 Task Libary ,例如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]
            });
            Thread.Sleep(1000);
            // 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();
        }
    

    Async和Await不适用于并行计算 . 它们用于不阻止主线程 . 如果是关于asp.net或Windows应用程序 . 由于网络呼叫阻止主线程是一件坏事 . 如果您这样做,您的应用将无法响应或可能会崩溃 .

    查看ms docs以获取一些示例 .

  • 138

    这里的所有答案都使用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;
        }
    
  • 105

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

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

    从而,

    • Main通过TestAsyncAwaitMethods启动长时间运行的方法 . 这会立即返回而不会暂停当前线程,我们会立即看到'Press any key to exit'消息

    • 所有这一切,LongRunningMethod在后台运行 . 完成后,来自Threadpool的另一个线程将获取此上下文并显示最终消息

    因此,不会阻止线程 .

  • 112

    在下面的代码中,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);
        // . . .
    }
    
  • 3

    For fastest learning..

    • 了解方法执行流程(附图):3分钟

    • 问题内省(学习缘故):1分钟

    • 快速通过语法糖:5分钟

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

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

    • 下一步去哪儿?

    Understand method execution flow(with a diagram): 3 mins

    在这张图片中,只关注#6

    在第6步:AccessTheWebAsync()已经完成了它可以做的工作,没有getStringTask的结果 . 因此,AccessTheWebAsync使用await运算符来暂停其进度并将控制权返回(yield)给调用者 . AccessTheWebAsync返回一个Task(of字符串返回值)给调用者 . 该任务表示生成字符串结果的承诺 . 但什么时候会回电呢?再打一次电话?

    AccessTheWebAsync()的调用者只做了等待(它可以完成一些内部任务,然后在需要时等待) . 所以调用者等待AccessTheWebAsync,而AccessTheWebAsync正在等待GetStringAsync .

    请记住,该方法已经返回,它不能再次返回(没有第二次) . 那么来电者怎么知道呢?这是关于 Tasks! 任务被退回 . Task was waited for (不是方法,不是 Value ) . 值将在Task中设置 . 任务状态将设置为完成 . 来电者只是监控任务 . 进一步读取以后here .

    Question introspection for learning sake: 1 min

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

    如何以及何时使用异步和等待任务?

    因为学习 Task 会自动覆盖其他2.为了学习至少 . 当然,这是关于 asyncawait 的问题的答案 .

    Quickly get through syntax sugar: 5 mins

    • 转换前(原始方法)

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

    我们完成了'awaiting'任务 . 因此 await . 由于我们使用await,我们必须使用async(强制)和MethodAsync与'Async'作为前缀(编码标准) . 稍后进一步读取here

    Share the confusion of a developer: 5 mins

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

    提示:尝试查找现有的Async实现(如 MethodAsyncToListAsync )以外包难度 . 所以我们只需要处理Async和等待(这很简单,非常类似于普通代码)

    Problem: Quickly change a real-world implementation of normal code to Async operation: 2 mins

    下面数据层中显示的代码行开始中断(许多地方) . 因为我们将一些代码从.Net framework 4.2更新为.Net核心 . 我们不得不在整个应用程序的1小时内解决这个问题!

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

    十分简单!

    • EntityFrameWork nuget(它具有QueryableExtensions)

    • namespace = Microsoft.EntityFrameworkCore

    代码改变了这样

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

    Contract GetContract(int contractnumber)

    async Task<Contract> GetContractAsync(int contractnumber)

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

    • 我们在30分钟内到处改变了!

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

    Where to Next? 有一个很棒的快速视频,我们可以看一下Converting Synchronous Calls to Asynchronous in ASP.Net Core,因为这可能是读完之后的方向 .

  • 1

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

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

    async和await关键字不会导致创建其他线程 . 异步方法不需要多线程,因为异步方法不能在自己的线程上运行 . 该方法在当前同步上下文上运行,并且仅在方法处于活动状态时才在线程上使用时间 .

  • 1

    进一步回答其他问题,看看await (C# Reference)

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

    以下Windows窗体示例说明了在异步方法WaitAsynchronouslyAsync中使用await . 将该方法的行为与WaitSynchronously的行为进行对比 . 如果没有将await运算符应用于任务,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";
    }
    
  • 592

    这是一个快速的控制台程序,以便跟随的人清楚 . “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);
            }
        }
    }
    

相关问题