个人观点,仅供参考
C#为开发者提供了可以说最好的异步API了,只需要Task.Run即可将原本同步耗时的API转化为异步API,但其中也有个"巨坑"
Demo
看一个简单的例子
public async Task<string> MyTaskAsync(Uri address)
{
return await Task.Run(() =>
{
using (var client = new WebClient())
{
return client.DownloadString(address);
}
});
}
乍一眼看这个函数似乎很正常,用一个Task包裹一个耗时的WebClient.DownloadString函数。但是我们需要问一下自己:
WebClient.DownloadString是CPU密集型,还是IO密集型- 在Task.Run所在的托管线程里,是阻塞的还是非阻塞的
答案很明确:IO密集型,并且仍然是阻塞的,那么这意味着什么:我们只是换了一个地方阻塞,也就是类似于拆东墙补西墙,这样的代码确实可以使UI不再卡顿,但实际上对于性能的可拓展并没有什么好处。
Solve
解决方案很简单,就是用系统提供的Async函数即可。
public async Task<string> MyTaskAsync(Uri address)
{
using (var client = new HttpClient())
{
return await client.GetStringAsync(address);
}
}
区别
无论是真异步,还是假异步,都要访问Web,来获取数据,区别在哪呢?
Throughout this entire process, a key takeaway is that no thread is dedicated to running the task. Although work is executed in some context (that is, the OS does have to pass data to a device driver and respond to an interrupt), there is no thread dedicated to waiting for data from the request to come back. This allows the system to handle a much larger volume of work rather than waiting for some I/O call to finish. (From MSDN)
一句话:真异步是没有专用线程等待的。
我们知道IO是很慢的,如果让一个线程去等待他,显然是错误的做法,这也是假异步的做法。我们需要的是系统把数据准备好了,然后在通知托管线程继续处理。在系统处理IO的时候,托管线程就可以想干啥就干啥,而不是傻等着。
Ref
[1.] Why you shouldn’t create asynchronous wrappers with Task.Run()
[2.] MSDN: async in depth