gpt4 book ai didi

c# - 为什么 BackgroundService.ExecuteAsync 中的阻塞调用不会阻塞主线程?

转载 作者:行者123 更新时间:2023-12-05 00:43:44 25 4
gpt4 key购买 nike

我在 Asp.Net Core Web API 中实现了一个 Microsoft.Extensions.Hosting.BackgroundService,它在 ExecuteAsync 内部有一个阻塞调用,但令人惊讶的是(我)它实际上并没有阻止我的应用程序,我想知道原因。

根据我能找到的BackgroundService源代码的不同版本,Task ExecuteAsync方法在StartAync<中被一劳永逸地调用了。来源如下。

        public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);

// If the task is completed then return it, this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}

// Otherwise it's running
return Task.CompletedTask;
}

据我了解,await 调用的继续(即下面的任何内容)将在调用此异步方法之后的相同 SynchronizationContext 中执行任务返回。如果这是真的,那么为什么这个延续代码(具有阻塞调用)不会阻塞我的应用程序?

举例说明:

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Yield();

Foo(); // Foo blocks its executing thread until an I/O operation completes.
}

由于永远不会等待 ExecutedAsync,因此方法的延续(即 Foo 调用)将在触发 ExecuteAsync 任务的同一同步上下文中执行第一名(如果我理解正确,它将在主线程中运行)。

我怀疑 Asp.Net 运行时必须有自己的 SynchronizationContext,它实际上在不同线程或类似的东西中执行异步延续。

任何人都可以在这里解释一下吗?

最佳答案

据我所知...我建议你阅读this首先。

现在,我假设你得到的 asp.net 核心本身实际上已经采用了无上下文的方法。

另外,当你构建主机时,通常是 CreateHostBuilder(args).Build().Run(); ,它实际运行主机,它是 IHost 的一个实例接口(interface),而不是 IHostedService , 其中 BackgroundService抽象类继承自。

简而言之,从 BackgroundService 继承的任何内容基类可以认为是一个特殊的对象,由宿主自己管理和执行,而不是与宿主自己使用的线程在同一级别上运行。

您可以在后台服务上放置一个阻塞线程的操作,但这是处理您的服务的线程,而不是应用程序线程本身。

更新

由于@underthevoid 希望进一步检查我所说的“与主机本身使用的线程不同的级别”,请允许我在这里澄清一些观点。

起初,CreateHostBuilder(args).Build().Run();构建 WebHost 的实例, 并通过调用 .Run() 来执行它,在屏幕后面调用 Start()方法如你所见here .

然后,这条线发生了奇迹_hostedServiceExecutor = _applicationServices.GetRequiredService<HostedServiceExecutor>(); ,它将调用 StartAsync(cancellationToken)在那之后。

现在,看看 the HostedServiceExecutor ,在构造函数上,它获取 IHostedService 的所有实例与您的应用一起注册,然后全部执行,正如您在代码中简要看到的那样。

IHostedService 中的每一个现在是执行自己的Tasks的对象,与处理WebHost的Task无关.现在它们都分开了。

我的意思是different level as the thread that got use by the host itself ,我试图指出两件事。

  • 所有BackgroundService您使用的被 WebHost 调用,没有它,他们什么也做不了。
  • 它们运行在单独的任务上,因此是单独的线程(我不认为一个线程可以同时处理多个任务,就像它们实现代码的方式一样)。

关于任务是如何执行的,一起来看看@Stephen Cleary 的回答,相信他胜过你自己,我也有同样的信念。

顺便说一句... this也是一些关于 BackgroundService 的好夫妻,我也从他们那里得到了很多东西。

希望这会有所帮助!

关于c# - 为什么 BackgroundService.ExecuteAsync 中的阻塞调用不会阻塞主线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68825893/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com