【问题标题】:Should I use Task.Run to wait tasks in a synchronous context?我应该使用 Task.Run 在同步上下文中等待任务吗?
【发布时间】:2018-08-17 09:38:40
【问题描述】:

我有一个 ASPX 页面,我无法将其转换为 async,但它在同步上下文中使用了一些 async 方法。它调用它们的方式是这样的:

public void MySyncMethod()
{
    var myTask = Task.Run(() => _myField.DoSomethingAsync());
    myTask.Wait();
    //use myTask.Result
}

async/await 和/或阻塞而言,这样做和以下有什么区别吗?

public void MySyncMethod()
{
    var myTask = _myField.DoSomethingAsync(); //just get the Task direct, no Task.Run
    myTask.Wait();
    //use myTask.Result
}

假设以前的开发人员添加了Task.Run 是有原因的。但是我在访问HttpContext 中的东西时遇到了问题,因为工作正在不同的线程上运行。

这里有使用Task.Run的理由吗?

【问题讨论】:

  • 如果它也是异步的,则不需要将您的任务添加到 Task.Run 中。 Task.Run 用于异步运行同步代码。底部是最好的方法,恕我直言。
  • 在可能的情况下,如果你不能让你的代码本身异步(你应该非常努力),如果系统同时提供同步和异步,请使用同步 API。你不会受益在这里你阻塞你的请求线程。

标签: c# asp.net async-await


【解决方案1】:

这样做和以下有什么区别吗 异步/等待和/或阻塞?

是的,第一个代码块使用线程池线程,然后等待它返回,因此您使用的不是一个线程。他们都挡住了。

我假设以前的开发人员添加 Task.Run 是有原因的。

是的,在 ASP.Net 上下文中阻止(直接)async 代码是 bad idea and can cause deadlocks。所以你的第二个代码块更有效(在线程使用方面)但会遇到严重的死锁问题。

这里的正确解决方案是使public void MySyncMethod() async 本身(public async Task MySyncMethod())。这两种解决方案都有缺点,唯一真正的出路是使整个调用堆栈async如果你能做到,那就去做吧。

如果您不能从另一个async 方法调用async 方法,那么Task.Run 是要走的路。详情请见How to call asynchronous method from synchronous method in C#?

如果你想在你的线程中使用HttpContext,请阅读Using HttpContext in Async Task,我肯定会赞成:

这些答案的选项并牢记

First off, you're not creating a copy of the object, you're just copying the reference to the object.HttpContext isn't a struct.....etc

【讨论】:

  • 我们不知道他们都阻止了。我们知道请求线程可以。在不知道 DoSomethingAsync 做什么的情况下,我们对其他线程一无所知。
  • 是的,他们做到了。通常,异步的全部意义在于阻塞线程。参见例如Steven Cleary 的优秀there is no thread
  • 或 Eric Lippert 从 2010 年开始的异步介绍之旅 - part two:“在该方法中使用了两次“await”运算符不是意味着“现在这个方法阻塞当前线程,直到异步操作返回”。这将使异步操作回到同步操作,这正是我们试图避免的。相反,它意味着相反 那个;"
  • 我自己也遇到过这个难题,你捡到了一些遗留代码,你想实现所有asyncasync 代码编写的 x埋在async之前的世界,心情沉重,伸手去拿Task.Run
  • ASP.NET HttpContext.Current inside Task.Run@thisextendsthat。重申一下,最佳解决方案是全部async 或没有async。其他一切充其量只是妥协。
【解决方案2】:

基于async/await 的异步代码的内部工作原理与Task.Run 启动的任务根本不同。 async/await 任务是基于承诺的,并且依赖于调用者在适当的时候将执行返回给异步方法。但是,Task.Run 启动的任务通常是在从线程池中获取的并行线程上启动的,并且不依赖调用者的合作来在适当的时候继续执行。

这种情况会导致一个问题,即您不能将基于 Promise 的任务视为与其他任务相同,因为基于 Promise 的任务可能会等待调用者合作返回执行,这可能永远不会发生,因为其他任务是独立执行并可能等待调用者。结果是死锁。

解决方案是specific Task.Run overload,它将为现有的基于任务的方法创建代理,以允许正确执行基于承诺的任务。在这个代理上调用Wait 是安全的。这就是其他开发人员使用此构造的原因。他还可以简化调用并避免使用这样的匿名方法:

var myTask = Task.Run(_myField.DoSomethingAsync);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-01
    • 1970-01-01
    • 2014-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多