大家在編程過程中都會用到一些異步編程的情況。在C#的BCL中,很多api都提供了異步方法,初學者可能對各種不同異步方法的使用感到迷惑,本文主要為大家梳理一下異步方法的變遷以及如何使用異步方法。
在.Net Framework 2.0中,最常見的方法是BeginXXX,和EndXXX這樣的方法來搭配使用。這種模式可以概括為方法+回調方法模式或者稱為InvokeMethod+EventHandler模式。
這種模型的基本流程是:
我們看一個FileStream的示例方法,在.Net 2.0中,你需要這樣使用異步:
using System;
using System.IO;
using System.Text;
public class AsyncTest
{
public static void Main(string[] args)
{
using (FileStream file = new FileStream("Test.txt", FileMode.OpenOrCreate))
{
var bytes = Encoding.UTF8.GetBytes("Test for .net framework 2.0");
IAsyncResult asyncResult = file.BeginWrite(bytes, 0, bytes.Length, callback, null);
file.EndWrite(asyncResult);
}
Console.ReadLine();
}
private static void callback(IAsyncResult ar)
{
Console.WriteLine("Finish Write");
}
}
從.Net 4.0開始,微軟引入了Task。由於Task本身的靈活性,也使得我們的異步編程模型更簡潔。上面的例子在.Net 4.5中可以這樣實現:
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
public class AsyncTest
{
public static void Main(string[] args)
{
using (FileStream file = new FileStream("Test.txt", FileMode.OpenOrCreate))
{
var bytes = Encoding.UTF8.GetBytes("Test for .net framework 4.5");
var task = file.WriteAsync(bytes, 0, bytes.Length);
task.Wait();
}
Console.ReadLine();
}
}
微軟在許多BCL的api中都添加了XXXAsync方法來實現新的異步模型。Task本身比回調方法靈活了許多,可以更優雅的實現回調,取消,調度等操作。關於Task的使用方式可以看我之前總結的文章link。
為了進一步簡化異步模型,微軟從Visual Studio 2012開始引入了async和await關鍵字。這個模型本身是基於編譯器的一個語法糖,編譯後會生成一個statemachine模型。這樣上面例子中的寫法也可以簡化成:
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
public class AsyncTest
{
public static void Main(string[] args)
{
TestFunc();
}
private static async void TestFunc()
{
using (FileStream file = new FileStream("Test.txt", FileMode.OpenOrCreate))
{
var bytes = Encoding.UTF8.GetBytes("Test for .net framework 4.5");
await file.WriteAsync(bytes, 0, bytes.Length);
}
}
}
如果大家注意看BCL中的類庫,會發現微軟並沒有在最新版本的類庫中對每一個BeginXXX的方法都添加了XXXAsync方法。這種情況下我們如何能讓新的異步模型兼容舊的方法呢?
以NamedPipeServerStream為例,這個類庫實現了一個管道的功能,微軟並沒有為其更新XXXAsync方法,你可以使用TaskFactory來兼容新的異步模型,你可以這樣來實現:
private static void OldAsyncModel()
{
NamedPipeServerStream pipe = new NamedPipeServerStream("customPipe", PipeDirection.InOut, -1, PipeTransmissionMode.Message, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
IAsyncResult async = pipe.BeginWaitForConnection(callback, null);
pipe.EndWaitForConnection(async);
}
private static async void NewAsyncModel()
{
NamedPipeServerStream pipe = new NamedPipeServerStream("customPipe", PipeDirection.InOut, -1, PipeTransmissionMode.Message, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
await Task.Factory.FromAsync(pipe.BeginWaitForConnection, pipe.EndWaitForConnection, null);
}
因此,我們可以總結為,.Net中有兩種異步編程模型:
BeginXXX模型微軟已經逐漸的考慮廢棄,返回Task的異步編程模型目前是微軟建議的方式。