【问题标题】:Do I use a JoinableTaskFactory with AspNetCore?我是否将 JoinableTaskFactory 与 AspNetCore 一起使用?
【发布时间】:2019-08-27 08:43:47
【问题描述】:

我已将 Microsoft.VisualStudio.Threading 导入 .Net Core Web App。我这样做是为了利用AsyncLazy<T>

我想确保我做对了,所以我导入了适当的Analyzers

警告和文档清楚地指出应该将JoinableTaskFactory 注入到我的实现中。

我的问题是,我应该如何在 .Net Core Web App 的配置中实例化 JoinableTaskFactory

就这么简单

public void ConfigureServices(IServiceCollection services)
{
    // ...
        services.AddSingleton(new JoinableTaskFactory());
    // ...
}

或者,这一切都错了吗?

【问题讨论】:

    标签: c# asp.net-core async-await


    【解决方案1】:

    是的,您可以在 ASP.NET Core 应用程序中使用 Microsoft.VisualStudio.Threading 库。但是,虽然JoinableTaskFactory 会在这样的应用程序中“工作”,但这是一个很好的迹象,你做错了什么。

    当然,分析器总是合适的,您看到的警告可能指出您不应该调用 Task.ResultTask.Wait() 等。这些同步阻塞线程并且可能严重降低您的网络应用程序(或任何应用程序)的可扩展性。您应该尽可能使用 await

    JoinableTaskFactory 可以在您无法使用 await 时介入,但您仍需要调用异步代码。使用JTF.Run 仍然会阻塞调用线程,但是当您在应用程序中有一个单线程SynchronizationContext 时,它会以一种避免死锁的方式这样做。我不认为 ASP.NET Core 有这样的东西,所以这不是问题。 JTF.Run 仍然比Task.Wait 更有效,因为它可以重用原始线程而不是第二个线程。

    如果您确实选择在您的 Web 应用程序中使用 JTF,如果 ASP.NET Core 不使用单线程 SynchronizationContext,那么您可以创建一个 JTF 实例并与整个应用程序。但如果它确实有一个单线程SynchronizationContext,那么每个网络请求都会有一个,这意味着您需要为每个请求创建一个新的JoinableTaskContext,因为这些都关联到一个SynchronizationContext。您总是从 JoinableTaskContext 实例中获取您的 JTF 实例。

    【讨论】:

    • 郑重声明,我现在认为我所做的一切都是错误的。我认为这就是您所说的,以更具建设性的方式。
    • 是的。随意更改已接受的答案。 :)
    • 您如何提供一个工作示例。那会很有用。构造函数有一些我以前从未见过的参数。这个兔子洞有多深?
    • 我们最近不得不修复一个 ASP.NET(Core?),其中 JoinableTaskContext 创建了一个 lot,它导致 .NET 的 CallContext 中的非常大的数组被浪费对象。您最多应该为每个请求创建 一个 JoinableTaskContext,而一个用于该过程的可能会好得多。只要您在SynchronizationContext.Current == null 时创建它,您就可以在任何地方重复使用它。而且由于它有一个Factory 属性,你甚至不必手动创建JoinableTaskFactory
    • @PeetBrits ThreadHelper.JoinableTaskFactory 实际上只在 Visual Studio 中可用,因为 ThreadHelper 是 VS SDK 的一部分。对于您自己的应用程序,您可以并且应该创建一个您自己的JoinableTaskContext 对象并在整个应用程序中使用该对象。您可以像 VS 通过自己的 ThreadHelper 类一样通过公共属性共享它。
    【解决方案2】:

    This page of the vs-threading docs

    ThreadHelper.JoinableTaskFactory 属性仅适用于在 VS 进程中运行的代码。如果您的代码用完了 proc(例如在 vstest.executionengine.exe 运行程序中),它将无法工作。

    因此,正如包的名称 Microsoft.VisualStudio.Threading 所暗示的,它旨在用于 Visual Studio 扩展。您为 AsyncLazy 的实现链接的代码使用 JoinableTaskFactory,因此它可能不适合在 Visual Studio 之外使用。我当然不会在需要切换到 UI 线程的 VS 扩展之外使用它。

    Steven Cleary's AsyncEx library 有一个AsyncLazythe wiki page 链接到这个Steven Toub blog post titled AsyncLazy。博客文章指出,惰性语义并没有真正增加 Task<T> 提供的内容,尽管由于值工厂在到达等待之前可能会做很多工作,他的示例在线程池上运行它并解开 @987654330 @。

    edit:正如 cmets 中所指出的,我从文档中引用的内容有些断章取义。但是,vs-threading 库是关于在使用带有同步上下文(主要是 GUI)的异步时避免死锁。问题的作者正在使用的 ASP.NET Core 没有同步上下文,因此不需要专门担心死锁主线程。虽然使用 vs-threading 库可能不会引起问题,正如我的引述所声称的那样,我仍然认为它不适合没有同步上下文的任何东西,并且有更好的选择,比如直接使用 Task<T> 而不是需要任何 AsyncLazy 实现。

    【讨论】:

    • 所以,我将做我应该做的事情,并改用Nito.AsyncEx
    • 我不同意这种说法。虽然引用的文档是关于专门测试 VS Extesions,但首页/自述文件说它是一个 xplat 库,我们知道 Visual Studio 只是在 Windows 上运行(Visual Studio for mac 只是更名为 Xamarin Studio) . github.com/Microsoft/vs-threading/blob/master/doc/… 对“具有 JoinableTaskContext 属性的单例类”部分和“接受 JoinableTaskContext 作为构造函数参数”的说明更加清楚。
    • 重要的是 newing JoinableTaskContext 发生在主线程(你希望你的东西被加入的那个),无论是主方法的线程,UI线程还是一些其他上下文(即 aspnet 核心或奥尔良调度上下文)
    • @Tseng 你说得对,我复制粘贴太快了。我编辑了我的答案以试图澄清我的立场,即在使用带有同步上下文的异步时使用旨在避免死锁的库并不是使用不使用同步上下文的框架时的最佳选择。
    • 这个答案是错误的。它引用的文档专门指的是ThreadHelper.JoinableTaskFactoryThreadHelper 类是在 VS SDK 中定义的,所以它当然只适用于 VS 扩展。但是JoinableTaskFactory 本身可以而且确实经常在 VS 之外使用。名称“VisualStudio”出现在包和程序集名称中,因为 VS 团队创建并维护了它。它与哪些类型的应用程序可以使用它无关。
    猜你喜欢
    • 1970-01-01
    • 2019-12-29
    • 2021-09-20
    • 2018-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多