{ using (var client = new WebClient()) { return client.DownloadString(address); } });}复制代码 乍一眼看这" />
当前位置: 首页 > >

C# 异步陷阱(Task.Run)

发布时间:

个人观点,仅供参考



C#为开发者提供了可以说最好的异步API了,只需要Task.Run即可将原本同步耗时的API转化为异步API,但其中也有个"巨坑"



Demo

看一个简单的例子


public async Task 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 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



转载于:https://juejin.im/post/5c365b3de51d45518c67bd6b



友情链接: