多个 AddHostedService dotnet 核心

btb*_*min 5 c# .net-core asp.net-core

当我试图注册不止一个AddHostedService方法StartAsync是调用仅在第一

services.AddHostedService<HostServiceBox>(); // StartAsync is called
services.AddHostedService<HostServiceWebSocket>(); // DO NOT WORK StartAsync not called
services.AddHostedService<HostServiceLogging>(); // DO NOT WORK StartAsync not called
Run Code Online (Sandbox Code Playgroud)

任何的想法?

感谢您的帮助

C-F*_*C-F 9

好吧,现在是 2022 年,.NET 6 已经发布了。如今,运行多个托管服务不是问题,只要它们由不同的类表示即可。像这样:

public class Program
{
    public static void Main(string[] args)
    {
        IHost host = Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Worker>();
                services.AddHostedService<Worker2>();
            }).Build();

        host.Run();
    }
}
Run Code Online (Sandbox Code Playgroud)

两个工人都会跑。

但是,如果我们需要同一服务类的多个实例并行运行怎么办?这似乎还是不可能的。

请参阅此处的相关讨论:https ://github.com/dotnet/runtime/issues/38751

我最终实现了自己的实用程序函数来并行启动多个任务并正确收集所有异常。这里:

/// <summary>
/// Runs multiple cancelable tasks in parallel. If any of the tasks terminates, all others are cancelled. 
/// </summary>
public static class TaskBunchRunner
{
    public class BunchException : Exception
    {
        public AggregateException Agg { get; }

        public BunchException(AggregateException agg) : base("Task bunch failed", agg)
        {
            Agg = agg;
        }

        public override string Message => $"Task bunch failed: {Agg.Message}";
        public override string ToString() => $"BunchException -> {Agg.ToString()}";
    }
    
    public static async Task Bunch(this IEnumerable<Func<CancellationToken, Task>> taskFns, CancellationToken ct)
    {
        using CancellationTokenSource combinedTcs = CancellationTokenSource.CreateLinkedTokenSource(ct);
        CancellationToken ct1 = combinedTcs.Token;

        Task[] tasks = taskFns.Select(taskFn => Task.Run(() => taskFn(ct1), ct1)).ToArray();
        
        // If any of the tasks terminated, it may be because of an error or a cancellation.
        // In both cases we cancel all of them. 
        await Task.WhenAny(tasks); // this await will never throw
        combinedTcs.Cancel();
        
        var allTask = Task.WhenAll(tasks); // this will collect exceptions in an AggregateException
        try
        {
            await allTask;
        }
        catch (Exception)
        {
            if (allTask.Exception != null) throw new BunchException(allTask.Exception);
            throw; 
        }
        
        // Why not just await Task.WhenAll() and let it throw whatever it is?
        // Because await will unwrap the aggregated exception and rethrow just one of the inner exceptions,
        // losing the information about others. We want all the exceptions to be logged, that is why 
        // we get the aggregated exception from the task. We also throw it wrapped into a custom exception, so the 
        // outer await (in the caller's scope) does not unwrap it again. :facepalm:
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们创建一个托管服务并使其ExecuteAsync方法将多个任务作为一组运行:

class MySingleService
{
    private readonly string _id;
    public MySingleService(string id){ _id = id;  }
    
    public async Task RunAsync(CancellationToken ct)
    {
        await Task.Delay(500, ct);
        Console.WriteLine($"Message from service {_id}");
        await Task.Delay(500, ct);
    }
}

class MyHostedService: BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        MySingleService[] individuals = new[]
        {
            new MySingleService("1"),
            new MySingleService("2"),
            new MySingleService("3"),
        };
        
        await individuals
            .Select<MySingleService, Func<CancellationToken, Task>>(s => s.RunAsync)
            .Bunch(stoppingToken);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        IHost host = Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<MyHostedService>();
            }).Build();

        host.Run();
    }
}
Run Code Online (Sandbox Code Playgroud)

注1:该类TaskBunchRunner取自真实项目并被证明有效,而使用示例是编造的,未经测试。

注2:该Bunch方法是为后台服务设计的,后台服务不会自然完成,它们会一直运行直到取消或失败。因此,如果一组任务中的一个成功完成,其他任务将被取消(这可能不是您想要的)。如果您需要完成支持,我建议检查以下结果WhenAny:如果比赛获胜者已跑完,我们需要将其从数组中删除,然后WhenAny再次进行。

我知道这并不完全是OP所要求的。但对于最终在这里遇到与我相同问题的人来说可能很有用。


btb*_*min 5

在工作代码下面,我通过创建一个助手来解决这个问题

@statup.cs

public void ConfigureServices(IServiceCollection services)
        {
            JwtBearerConfiguration(services);

            services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
            {
                builder
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowAnyOrigin()
                    .AllowCredentials();
            }));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ;


            services.AddSignalR();

            services.AddHostedService<HostServiceHelper>(); // <===== StartAsync is called

        }
Run Code Online (Sandbox Code Playgroud)

@HostServiceHelper.cs

    public class HostServiceHelper : IHostedService
    {
        private static IHubContext<EngineHub> _hubContext;

        public HostServiceHelper(IHubContext<EngineHub> hubContext)
        {
            _hubContext = hubContext;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            return Task.Run(() =>
            {
                Task.Run(() => ServiceWebSocket(), cancellationToken);

                Task.Run(() => ServiceBox(), cancellationToken);

                Task.Run(() => ServiceLogging(), cancellationToken);

            }, cancellationToken);
        }

        public void ServiceLogging()
        {
        // your own CODE
         }

        public void ServiceWebSocket()
        {
 // your own CODE
        }

        public void ServiceBox()
        {
            // your own CODE
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            //Your logical
            throw new NotImplementedException();
        }
    }
Run Code Online (Sandbox Code Playgroud)