C# 异步陷阱(Task.Run)

原文链接: https://juejin.im/post/5c365b3de51d45518c67bd6b

个人观点,仅供参考

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函数。但是我们需要问一下自己:

  1. WebClient.DownloadString 是CPU密集型,还是IO密集型
  2. 在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

你可能感兴趣的:(C# 异步陷阱(Task.Run))