async-awaitasync-await入门


备注

async-await允许异步(意味着非阻塞,并行)代码执行。它有助于始终保持UI响应,同时在后台运行可能很长的操作。

它对I / O操作特别有用(例如从服务器下载或从HDD读取文件),但它也可用于执行CPU密集型计算而不会冻结您的应用程序。

异步无效

您可以使用void (而不是Task )作为异步方法的返回类型。这将导致“即发即忘”动作:

public void DoStuff()
{
    FireAndForgetAsync();
}
    
private async void FireAndForgetAsync()
{
    await Task.Delay(1000);
    throw new Exception(); //will be swallowed
}
 

当您返回void ,您无法await FireAndForgetAsync 。您将无法知道方法何时完成,并且将吞下async void 方法中引发的任何异常。

执行同步代码异步

如果要异步执行同步代码(例如CPU扩展计算),可以使用Task.Run(() => {})

public async Task DoStuffAsync()
{
    await DoCpuBoundWorkAsync();
}


private async Task DoCpuBoundWorkAsync()
{
    await Task.Run(() =>
    {
        for (long i = 0; i < Int32.MaxValue; i++)
        {
            i = i ^ 2;
        }
    });
}
 

简单的用法

使用async-await 需要三件事:

  • Task 对象:该对象由异步执行的方法返回。它允许您控制方法的执行。
  • await 关键字:“等待” Task 。将此关键字放在Task 之前,以异步方式等待它完成
  • async 关键字:所有使用await 关键字的方法都必须标记为async

一个小例子,演示了这个关键字的用法

public async Task DoStuffAsync()
{
    var result = await DownloadFromWebpageAsync(); //calls method and waits till execution finished
    var task = WriteTextAsync(@"temp.txt", result); //starts saving the string to a file, continues execution right await
    Debug.Write("this is executed parallel with WriteTextAsync!"); //executed parallel with WriteTextAsync!
    await task; //wait for WriteTextAsync to finish execution
}

private async Task<string> DownloadFromWebpageAsync()
{
    using (var client = new WebClient())
    {
        return await client.DownloadStringTaskAsync(new Uri("http://stackoverflow.com"));
    }
}

private async Task WriteTextAsync(string filePath, string text)
{
    byte[] encodedText = Encoding.Unicode.GetBytes(text);

    using (FileStream sourceStream = new FileStream(filePath, FileMode.Append))
    {
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
    }
} 
 

有些事情需要注意:

  • 您可以使用Task<string> 或类似操作从异步操作指定返回值。 await 关键字等待,直到方法的执行完成并返回string
  • Task 对象只包含方法执行的状态,它可以用作任何其他变量。
  • 如果抛出异常(例如WebClient ),它会在第一次使用await 关键字时冒泡(在此示例中为line var result (...)
  • 建议命名将Task 对象作为MethodNameAsync 返回的方法

Task对象

如果你拿走async-await 关键字, Task 对象就像任何其他对象一样。

考虑这个例子:

public async Task DoStuffAsync()
{
    await WaitAsync();
    await WaitDirectlyAsync();
}

private async Task WaitAsync()
{
    await Task.Delay(1000);
}

private Task WaitDirectlyAsync()
{
    return Task.Delay(1000);
}
 

这两种方法的区别很简单:

  • WaitAsync 等待Task.Delay 完成,然后返回。
  • WaitDirectlyAsync 不等待,只是立即返回Task 对象。

每次使用await 关键字时,编译器都会生成处理它的代码(以及它等待的Task 对象)。

  • 在调用await WaitAsync() 这会发生两次:一次在调用方法中,一次在方法本身中。
  • 在调用await WaitDirectlyAsync 这只发生一次(在调用方法中)。因此,与await WaitAsync() 相比,您将存档一点加速。

小心exceptions :在第一次await Task 时, Exceptions 会冒出来。例:

private async Task WaitAsync()
{
    try
    {
        await Task.Delay(1000);
    }
    catch (Exception ex)
    {
        //this might execute
        throw;
    }
}

private Task WaitDirectlyAsync()
{
    try
    {
        return Task.Delay(1000);
    }
    catch (Exception ex)
    {
        //this code will never execute!
        throw;
    }
}