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

当前回答

这里的答案可以作为await/async的一般指导。它们还包含一些关于await/async如何连接的细节。我想和大家分享一些在使用这个设计模式之前应该知道的实践经验。

术语“await”是字面意义上的,所以无论您在哪个线程上调用它,都将在继续之前等待该方法的结果。在前台线程上,这是一个灾难。前台线程承担了构建应用程序的负担,包括视图、视图模型、初始动画,以及其他任何与这些元素捆绑在一起的东西。所以当你等待前台线程时,你会停止应用程序。当什么都没有发生时,用户会一直等待。这提供了一种消极的用户体验。

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

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

其他回答

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

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

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),然后等待longRunningTask到达。 现在,如果longRunningTask还没有完成,它仍在运行,MyMethodAsync()将返回到它的调用方法,因此主线程不会被阻塞。当longRunningTask完成时,来自ThreadPool的线程(可以是任何线程)将返回到MyMethodAsync()之前的上下文中并继续执行(在这种情况下将结果打印到控制台)。

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

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

我们应该只在需要立即得到结果时使用async & await。也许函数返回的结果将在下一行中使用。

关注这个博客,它用简单的文字写得很好

这里有一个快速的控制台程序,让那些遵循。TaskToDo方法是你想让它异步的长期运行的方法。让它以异步方式运行是由TestAsync方法完成的。test loops方法只是运行TaskToDo任务,并异步运行它们。你可以在结果中看到这一点,因为它们在每次运行中完成的顺序不同——当它们完成时,它们会报告给控制台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);
        }
    }
}

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

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

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

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

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