在Visual Studio发布以来,新增了大量的功能,其中最让我感到欣喜的是C#新增的利用async标识符和await相结合使用进行异步编程,当然,遗憾的是,此功能只支持WinForm和WPF程序。
使用 async 功能,您可以调用异步方法,而不定义持续性任务或通过多个方法或 lambda 表达式拆分代码。其实,仔细想想,其底层应该是封装了开辟新线程、调用和回调的操作!
回想下,在先前的WinForm和WPF的同步编程,比如我们在当前UI下进行一个非常耗时的操作的时候,窗体的表现是不能随意拖动和关闭的,必须等待当前的操作完成之后,才能执行其他的操作,给用户的体验非常差。在以前的版本中,要实现异步操作,必须手动去启动窗体的UI线程允许跨进程调用的开关,并且把需要进行异步操作的代码写在一个委托中,然后手动的去新开线程,来完成异步的操作。
然后,新版的Visual C#封装了这些麻烦的操作,让我们进行异步编程变得非常的简单。只需要利用async和await相结合使用,就可以轻松的异步编程。
在下面的两个例子中,将详细的讲述如何利用async和await来进行异步编程。
第一个例子,是一个简单的WPF程序,其主要功能是异步去请求几个URL,并得到这些资源的字符个数,然后输出在相应的文本框中。
新建一个WPF应用程序,添加一个按钮和文本框,文本框多行显示。给按钮注册一个异步的Click方法。标志按钮的Click事件是一个异步方法,只需要添加标示符async。
private async void startButton_Click(object sender, RoutedEventArgs e)
{
resultsTextBox.Clear();
await SumPageSizesAsync();
Task sumTask = SumPageSizesAsync();
await sumTask;
//上面的两句话等价于:await SumPageSizesAsync();
resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";
}
private async Task SumPageSizesAsync()
{
//.Net Framework4.5中新增了一个新的类HttpClient,这个类封装异步web操作,非常的牛B
//当然我们也可以用传统的WebRequest和HttpWebRequest类。
HttpClient client = new HttpClient();
List<string> urlList = SetUpURLList();
var total = 0;
foreach (var url in urlList)
{.
byte[] urlContents = await client.GetByteArrayAsync(url);
//上面的一句话等价于下面的两句话
//Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
//byte[] urlContents = await getContentsTask;
DisplayResults(url, urlContents);
total += urlContents.Length;
}
resultsTextBox.Text +=
string.Format("\r\n\r\nTotal bytes returned: {0}\r\n", total);
}
private List<string> SetUpURLList()
{
List<string> urls = new List<string>
{
"http://msdn.microsoft.com/library/windows/apps/br211380.aspx",
"http://msdn.com",
"http://msdn.microsoft.com/en-us/library/hh290136.aspx",
"http://msdn.microsoft.com/en-us/library/ee256749.aspx",
"http://msdn.microsoft.com/en-us/library/hh290138.aspx",
"http://msdn.microsoft.com/en-us/library/hh290140.aspx",
"http://msdn.microsoft.com/en-us/library/dd470362.aspx",
"http://msdn.microsoft.com/en-us/library/aa578028.aspx",
"http://msdn.microsoft.com/en-us/library/ms404677.aspx",
"http://msdn.microsoft.com/en-us/library/ff730837.aspx"
};
return urls;
}
private void DisplayResults(string url, byte[] content)
{
// Display the length of each website. The string format
// is designed to be used with a monospaced font, such as
// Lucida Console or Global Monospace.
var bytes = content.Length;
// Strip off the "http://".
var displayURL = url.Replace("http://", "");
resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
}
有关HttpClient和Task、Task<TResult>请参考MSDN。
编译WPF应用程序,在程序异步执行请求操作的时候,你可以随意的拖动窗口或者随时都可以关闭窗口,是不是觉得很方便,体验也很爽。想了解更详细的信息,告诉你一个秘籍,赶紧看MSDN,并且撞上windows8和Visual Studio2012开始你的奇妙之旅吧。
第二例子是文件I/O的异步操作,下面的例子包括了异步读取操作、异步拷贝操作、异步写操作。更多地文件I/O异步操作,建议你放看MSDN。
新建一个WinForm程序,添加三个按钮,来分别执行异步读取操作、异步拷贝操作、异步写操作。
在上面的WPF例子中,讲述了如何为按钮注册一个异步事件。就是用async标示Click事件。
//异步读
private async void btnReadAsync_Click(object sender, EventArgs e)
{
string sourceDir = @"C:\我的文件夹";
this.txtContentsByReadAsync.Clear();
foreach (string item in Directory.EnumerateFiles(sourceDir))
{
string fileContents="";
using (StreamReader sr=File.OpenText(item))
{
fileContents= sr.ReadToEnd();
}
using (FileStream stream=File.Open(item, FileMode.Open))
{
byte[] buffer=new byte[stream.Length];
int num= await stream.ReadAsync(buffer, 0, buffer.Length);
this.txtContentsByReadAsync.Text += string.Format("读取了{0}个字节\r\n,文件的内容为:\r\n{1}", num.ToString(), fileContents);
}
}
}
//异步拷贝
private async void btnCopyToAsync_Click(object sender, EventArgs e)
{
string startDir = @"C:\我的文件夹";
string destinationDir = @"C:\我的文件夹\新建文件夹\";
foreach (string item in Directory.EnumerateFiles(startDir))
{
using (FileStream fileStream=File.Open(item, FileMode.Open))
{
using (FileStream Stream=File.Create(destinationDir+item.Substring(item.LastIndexOf('\\'),item.Length-item.LastIndexOf('\\'))))
{
await fileStream.CopyToAsync(Stream);
}
}
}
}
//异步写
private async void btnWriteAsync_Click(object sender, EventArgs e)
{
string sourceDir = @"C:\我的文件夹";
foreach (string item in Directory.EnumerateFiles(sourceDir))
{
using (StreamReader reader=File.OpenText(item))
{
using (StreamWriter writer=File.CreateText(sourceDir+"\\"+"新建文件夹"+"\\"+"复制文件"+item.Substring(item.LastIndexOf('\\')+1)))
{
await CopyFilesAsync(reader, writer);
}
}
}
}
private async Task CopyFilesAsync(StreamReader reader, StreamWriter writer)
{
char[] buffer = new char[0x1000];
int num;
while ((num = await reader.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
await writer.WriteAsync(buffer, 0, num);
}
}
从上面的例子中可以看出,进行文件的异步I\O操作已经变得如此简单。
注意,要使用await异步编程,代码必须放在aysnc标识符标识的异步方法内部。对于控件的事件,只需要用async来标识即可。对于普通的方法,如果方法的返回值为空或者null的时候,用async和task一起标识该方法是无返回值的方法,对于返回泛型类型的方法来说,用async和task<T>来同时标志方法是异步方法,其中T为返回的类型。
上面的WPF例子用到了.Net Framework提供的新的类HttpClient,要使用这个类,程序中必须引用组件:
System.Net.Http组件。