首页 > 解决方案 > 当您阻塞同步方法的异步包装器时,资源会发生什么情况?

问题描述

我在我们的生产代码库中看到了一些遵循如下模式的奇怪代码。我想知道何时Start()调用与直接运行同步代码对性能的影响是什么。特别是在这种情况下如何管理任务或线程。

public Task RunLongTask(){
    return Task.Run(()=>{
       // Run some Synchronous long IO process
    }
}

public void Start(){
    RunLongTask().Wait();
}

我的想法是否正确,在等待同步 IO 调用完成时会阻止一项任务。并且来电者RunLongTask()也将被阻止。本质上是直接运行同步代码的成本翻倍。由于双重阻塞,在这种情况下是否总是会创建 2 个线程?

标签: c#asynchronoustask

解决方案


Task.RunThreadPool根据MSDN ,方法将指定的工作排入队列以在 上运行。Task.Wait方法是阻塞一个。可以在这篇文章中找到对此的简单解释

即使底层任务是异步的,如果您在任务上调用阻塞方法或阻塞属性,执行将等待任务完成 - 但会同步执行,这样当前线程在等待期间完全被占用。因此,如果您使用上述属性/方法之一,请确保这实际上是您想要做的。

因此,您当前的线程(执行RunLongTask().Wait();)将被阻塞,直到其中Task的工作同步完成

Task.Run(()=>{
       // Run some Synchronous long IO process
    }

直接运行同步代码不会加倍,您的主线程只是被阻塞并等待直到 I/O 进程在单独的线程上完成

由于双重阻塞,在这种情况下是否总是会创建 2 个线程?

你已经至少有一个Thread,当前的,它正在调用Task.RunTask.Run将从线程池中获得一个空闲的工作线程(如果有的话),但它将是一个不同的线程。

就性能而言,在单独的线程上运行 I/O 操作效率不高(因为它可能用于 CPU 密集型工作),因为工作线程将主要等待发送/接收数据。最好让你的长 IO 进程异步并在不阻塞的情况下运行它(当然,如果它支持异步 API)。

另一个潜在的性能缺陷是 ASP.NET Core 每个请求使用一个线程,并且运行具有长时间运行 IO 进程的后台线程将减少可用于处理新请求的线程数量(以及应用程序的可伸缩性)


推荐阅读