6

.net core BackgroundServiceorIHostedService的启动方法是异步的:

//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
    }
}

语义上我认为第二种似乎是对的,但是如果有几个IHostedServices,它们可以与第二种形式并行运行吗?

编辑 1

我还编写了一个示例程序来说明托管服务本身并不是作为单独的线程运行的。

在我输入 a 之前,该消息"Waiting for signal.."不会被写入控制台q

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;
        }
    }
}
4

2 回答 2

9

只需使用async/来实现它await,如您的第二个示例所示。不需要额外的Thread.

旁注:Thread应仅用于 COM 互操作;如今,对于Thread. 只要您键入new Thread,您就已经有了旧代码。

于 2019-06-10T13:20:56.080 回答
3

StartAsync()至少,您应该启动一个单独的线程。如果您的工作需要很长时间才能完成。否则——至少根据我的经验,在 Windows 10 上使用 .NET Core 2.1——在IHostedService所有工作完成之前不会注册为“已启动”。

我有一个测试控制台应用程序,它 HTTP-GET 相同的 URL 360 次,每次之间等待 10 秒,并计算它成功的次数和失败的次数。在这个应用程序中,我使用 配置ILogger了日志记录NLog,同时转到文本文件和控制台。每个日志记录的最高级别。

托管应用程序完成启动后,我会看到一条消息记录到控制台(和文件),告诉我托管应用程序已启动,我可以按 Ctrl+C 退出。

如果在 中StartAsync(),我只是完成所有工作的测试方法,那么在一个多小时后完成所有工作await之前,我不会看到记录该消息。相反,如果我像您的第一个示例一样启动一个新线程,那么我应该立即看到 Ctrl+C 指令。

所以从经验上看,你的第一个例子似乎是正确的。

于 2021-01-08T12:40:52.407 回答