【问题标题】: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”运算符不是意味着“现在这个方法阻塞当前线程,直到异步操作返回”。这将使异步操作回到同步操作,这正是我们试图避免的。相反,它意味着相反 那个;"
  • 我自己也遇到过这个难题,你捡到了一些遗留代码,你想实现所有async、async 代码编写的 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
    相关资源
    最近更新 更多