【问题标题】:What happens to the resources when you block on a asynchronous wrapper for a synchronous method?当您阻塞同步方法的异步包装器时,资源会发生什么情况?
【发布时间】:2019-11-30 09:37:03
【问题描述】:

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

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

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

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

【问题讨论】:

标签: c# asynchronous task


【解决方案1】:

Task.Run 方法根据MSDN 将指定工作排队以在ThreadPool 上运行。 Task.Wait 方法正在阻止一个。对此的简单解释可以看这个article

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

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

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-11
    • 2018-04-06
    • 2012-01-15
    • 1970-01-01
    • 2018-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多