可以说我们有一个ASP.NET Core,它接收一个字符串作为有效载荷,大小顺序为几兆字节。第一种方法的实现:
[HttpPost("updateinfos")]
public async Task UpdateInfos()
{
var len = (int)this.Request.ContentLength;
byte[] b = new byte[len];
await this.Request.Body.ReadAsync(b,0,len);
var content = Encoding.UTF8.GetString(b);
.....
}
使用ReadAsync
读取正文,这很好,因为我们在套接字上具有I / O内容,并且由于调用本身的性质,使其异步是免费的。但是,如果我们看管的话,GetString()
方法纯属CPU,具有线性复杂度。无论如何,这会影响性能,因为其他客户端正在等待我的字节转换为字符串。我想避免这种情况的解决方案是通过以下方式在线程池上运行GetString()
:
[HttpPost("updateinfos")]
public async Task UpdateInfos()
{
var len = (int)this.Request.ContentLength;
byte[] b = new byte[len];
await this.Request.Body.ReadAsync(b,0,len);
var content = await Task.Run(()=>ASCIIEncoding.UTF8.GetString(b));
.....
}
请不要介意现在的返回,该函数中还需要做更多的事情。
所以问题是,第二种方法是否会导致过度杀伤?如果是这样,则有什么界限来区分哪些可能作为阻塞运行,哪些必须移至另一个线程?
答案 0 :(得分:4)
您非常滥用Task.Run
。 Task.Run
用于将工作卸载到另一个线程上,并异步等待其完成。因此,每个Task.Run
调用都将导致线程上下文切换。当然,对于不应该在自己的线程上运行的事情来说,通常这是一个非常糟糕的主意。
诸如ASCIIEncoding.UTF8.GetString(b)
之类的东西非常快。创建和管理一个封装该线程的线程所涉及的开销比直接在同一线程上直接执行要大得多。
通常应该仅将Task.Run
用于减轻(主要是同步的)工作量,这些工作可以受益于在自己的线程上运行。或者,在某些情况下,您的工作可能会花费一些时间,但会阻止当前执行。
在您的示例中,事实并非如此,因此您应该同步调用这些方法。
如果您真的想减少该代码的工作量,则应查看如何正确使用流。在代码中执行的操作将完全读取请求正文流,然后您才能对其进行处理(尝试转换为字符串)。
您可以直接使用StreamReader将流作为字符串读取,而不是将读取二进制流然后转换成字符串的过程分开。这样,您可以直接读取请求主体,甚至可以异步读取。因此,根据您以后实际使用它的情况,这可能会更有效率。