好的,现在是2023年,.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();
}
}
两个工作线程都会运行。
但是如果我们需要并行运行同一个服务类的多个实例呢?这似乎仍然是不可能的。
请参见相关讨论:https://github.com/dotnet/runtime/issues/38751
最终我编写了自己的实用函数,以启动多个任务并正确收集所有异常。代码如下:
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();
await Task.WhenAny(tasks);
combinedTcs.Cancel();
var allTask = Task.WhenAll(tasks);
try
{
await allTask;
}
catch (Exception)
{
if (allTask.Exception != null) throw new BunchException(allTask.Exception);
throw;
}
}
}
现在我们创建一个单一的托管服务,并使其
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();
}
}
注意事项1:类
TaskBunchRunner
已从真实项目中取出并证明有效,而使用示例是虚构的并未经测试。
注意事项2:方法
Bunch
设计用于后台服务,这些服务不会自然完成,它们会一直运行直到被取消或失败。因此,如果一组中的一个任务成功完成,其他任务将被取消(这可能不是您想要的)。如果需要支持完成操作,建议检查
WhenAny
的结果:如果竞赛获胜者已经完成了,我们需要将其从数组中删除并再次调用
WhenAny
。
我知道这不完全符合OP的要求,但可能对那些遇到与我相同问题的人有用。
Task.CompletedTask
? - 321XAddHostedService
,像这样:services.AddSingleton<IHostedService, MyService>();
来源:https://forums.asp.net/t/2156818.aspx?How+to+run+2+services+in+one+time+ - Bob Horn