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

当前回答

我想对此发表我的意见,如果任何其他答案包含我将解释的内容,我很抱歉,我读了大部分,但没有找到它,但我可能错过了一些东西。

我看到了很多错误的概念和很多好的解释,只是想解释一下异步与并行编程的区别,我相信这会让事情更容易理解。

当你需要做长时间的计算,处理器密集的工作,你应该选择使用并行编程,如果可能的话,以优化核心的使用。这将打开一些线程并同时处理一些事情。

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

异步编程在不同的用例中使用。

当你在等待一些不依赖于你的处理器的事情时,它用来释放你的线程,例如IO(写入和读取磁盘),当你执行IO时,你的线程什么都不做,当你等待一个昂贵的查询结果从DB返回时也是一样。

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

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

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

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

这就是为什么Entity Framework有一个async api来保存、列表、查找等等…

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

希望这对某些人有用…

其他回答

我想对此发表我的意见,如果任何其他答案包含我将解释的内容,我很抱歉,我读了大部分,但没有找到它,但我可能错过了一些东西。

我看到了很多错误的概念和很多好的解释,只是想解释一下异步与并行编程的区别,我相信这会让事情更容易理解。

当你需要做长时间的计算,处理器密集的工作,你应该选择使用并行编程,如果可能的话,以优化核心的使用。这将打开一些线程并同时处理一些事情。

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

异步编程在不同的用例中使用。

当你在等待一些不依赖于你的处理器的事情时,它用来释放你的线程,例如IO(写入和读取磁盘),当你执行IO时,你的线程什么都不做,当你等待一个昂贵的查询结果从DB返回时也是一样。

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

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

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

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

这就是为什么Entity Framework有一个async api来保存、列表、查找等等…

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

希望这对某些人有用…

我的理解是,还应该有第三个术语:任务。

Async只是你加在方法上的一个限定词,表示它是一个异步方法。

Task是async函数的返回值。它是异步执行的。

您等待一个任务。当代码执行到这一行时,控制权跳回周围原始函数的调用者。

如果相反,你将一个异步函数(即任务)的返回值赋给一个变量,当代码执行到这一行时,它只是在任务异步执行时继续在周围的函数中越过这一行。

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

基本思想是,您拥有一个单独的异步执行任务的线程池。使用时。然而,对象承诺它将在某个时间执行操作,并在您请求时给您结果。这意味着当您请求结果并且还没有完成时,它将阻塞,否则将在线程池中执行。

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

实际上,您可以在c# 4.0中实现这种未来模式。如果你想知道它到底是如何工作的,我可以推荐这个链接:http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/。但是,如果您自己开始使用它,您将注意到如果您想做所有很酷的事情,您确实需要语言支持——这正是微软所做的。

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

它们是为了让异步代码易于编写和阅读。

这和生成后台线程来执行长时间逻辑是一样的吗?

一点也不。

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

async关键字启用await关键字。所以任何使用await的方法都必须被标记为async。

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

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

//是否在后台线程上执行?

No.


你可能会发现我的async/await介绍很有用。官方MSDN文档也非常好(尤其是TAP部分),异步团队还发布了一个很好的FAQ。

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