.NET Core中BackgroundService
或者IHostedService
的启动方法是异步的:
//IHostedService
Task StartAsync(CancellationToken cancellationToken);
//BackgroundService
Task ExecuteAsync(CancellationToken stoppingToken);
那么我应该在
ExecuteAsync
/StartAsync
方法中编写所有逻辑,还是应该立即启动新线程并立即返回?例如,以下两种实现方式哪种是正确的?
1.
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
new Thread(async () => await DoWork(stoppingToken)).Start();
await Task.CompletedTask;
}
private async Task DoWork(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
//actual works
}
2.
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
//actual works
await Task.Delay(1000);//e.g
}
}
从语义上讲,第二个似乎是正确的,但如果有多个IHostedService
,它们能够并行运行吗?
编辑1
我还编写了一个示例程序,说明托管服务并不是作为独立线程运行。
在输入q
之前,控制台不会显示消息"Waiting for signal.."
:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace BackgroundTaskTest
{
public class Program
{
public static async Task Main(string[] args)
{
var host = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
IConfiguration config = hostContext.Configuration;
//register tasks
services.AddHostedService<ReadService>();
services.AddHostedService<BlockService>();
})
.UseConsoleLifetime()
.Build();
await host.RunAsync();
}
}
public static class WaitClass
{
public static AutoResetEvent Event = new AutoResetEvent(false);
}
public class ReadService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var c = Console.ReadKey();
if (c.KeyChar == 'q')
{
Console.WriteLine("\nTrigger event");
WaitClass.Event.Set();
}
await Task.Delay(1);
}
}
}
public class BlockService : BackgroundService
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine("Waiting for signal..");
WaitClass.Event.WaitOne();
Console.WriteLine("Signal waited");
}
return Task.CompletedTask;
}
}
}
await
操作被触发时,托管服务会进入一个独立的线程。这是异步操作的行为,它可能会将这些异步操作放到独立的线程中,但我认为不能保证await
会将操作放到独立的线程中... - mosakashakaawait
后面的代码都是 continuation,它们可能会在另一个线程中运行,也可能不会。从我的测试代码来看,所有主机服务都在同一个线程中运行(因为前面的服务将阻塞后面的服务),只有使用await Task.Delay(xxx)
后面的服务才会运行。延迟超时后,前面的服务 可能 与后面的服务并行运行,因为前面的服务是 continuation,可能在单独的线程中运行。但正如你所提到的,这并不是保证的,所以我仍然需要自己创建线程... - mosakashaka