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

当前回答

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

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

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

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

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

其他回答

我认为你用System.Threading.Thread.Sleep选了一个不好的例子

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

System.Threading.Thread.Sleep不是“正在完成”的事情,它只是休眠,因此你的下一行在5秒后到达……

阅读这篇文章,我认为它很好地解释了async和await概念:http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx

除了其他答案,还有await (c#参考)

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

下面的Windows窗体示例说明了await在 异步方法,WaitAsynchronouslyAsync。对比一下它的行为 方法使用waitsynchrontically的行为。没有等待 应用到任务的操作符,waitsynchronize同步运行 尽管在定义中使用了async修饰符,并且调用了 线程。睡在它的身体里。

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

异步/等待

实际上,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应用程序时,由于网络调用阻塞主线程是一件糟糕的事情。如果你这样做,你的应用程序将得不到响应,甚至崩溃。

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

在更高的层次上:

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

    }
}

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