C# 同时启动多个长期运行的后台服务
我正在一个C# 同时启动多个长期运行的后台服务,c#,.net-core,background-process,asp.net-core-hosted-services,C#,.net Core,Background Process,Asp.net Core Hosted Services,我正在一个dotnetcore2.2中试验IHostedService。我的任务是创建两个后台长时间运行的任务 第一个是管理Selenium浏览器会话(打开/关闭选项卡,解析DOM),并将电子邮件消息放入ConcurrentBag内的队列中 第二个后台工作人员每10分钟发送一次电子邮件通知,通知存在于ConcurrentBag中的消息(第一个任务添加了该消息)。它还将它们分组,以便只发送一条消息 但是,我在同时运行2个托管进程时遇到问题。似乎只有第一个托管进程被执行,而第二个进程等待第一个进
dotnetcore2.2
中试验IHostedService
。我的任务是创建两个后台长时间运行的任务
- 第一个是管理
浏览器会话(打开/关闭选项卡,解析DOM),并将电子邮件消息放入Selenium
内的队列中ConcurrentBag
- 第二个后台工作人员每10分钟发送一次电子邮件通知,通知存在于
中的消息(第一个任务添加了该消息)。它还将它们分组,以便只发送一条消息ConcurrentBag
IHostedService
?如果是,那么完成任务的最佳体系结构方法是什么
以下是我当前正在使用的代码(正在尝试完成):
EmailWorkerHostedService
public class EmailWorkerHostedService : BackgroundService
{
private readonly EmailSender _emailSender;
private readonly IHostingEnvironment _env;
public EmailWorkerHostedService(
EmailSender emailSender,
IHostingEnvironment env)
{
_emailSender = emailSender;
_env = env;
}
protected override async Task ExecuteAsync(CancellationToken stopToken)
{
while (!stopToken.IsCancellationRequested)
{
var builder = new StringBuilder();
List<string> exceptionMessages = new List<string>();
string exceptionMessage;
while (Program.Messages.TryTake(out exceptionMessage))
exceptionMessages.Add(exceptionMessage);
if (exceptionMessages.Any())
{
foreach (var message in exceptionMessages)
{
builder.AppendLine(new string(message.Take(200).ToArray()));
builder.AppendLine();
}
string messageToSend = builder.ToString();
await _emailSender.SendEmailAsync(messageToSend);
}
Thread.Sleep(10000);
}
}
}
公共类EmailWorkerHostedService:BackgroundService
{
私人只读EmailSender\u EmailSender;
私有只读IHostingEnvironment;
公共EmailWorkerHostedService(
EmailSender EmailSender,
IHostingEnvironment(环境)
{
_emailSender=emailSender;
_env=env;
}
受保护的覆盖异步任务ExecuteAsync(CancellationToken stopToken)
{
而(!stopToken.IsCancellationRequested)
{
var builder=新的StringBuilder();
列表例外消息=新列表();
字符串例外消息;
while(Program.Messages.TryTake(out exceptionMessage))
添加(exceptionMessage);
if(exceptionMessages.Any()除外)
{
foreach(异常消息中的var消息)
{
AppendLine(新字符串(message.Take(200.ToArray());
AppendLine();
}
字符串messageToSend=builder.ToString();
wait\u emailSender.sendmailasync(messageToSend);
}
睡眠(10000);
}
}
}
编辑:在应用了答案中建议的更改后,下面是代码的当前版本。添加
wait
有帮助。首先不要在异步上下文中使用Thread.Sleep()
,因为它是阻塞操作。改用Task.Delay()
。我相信这是你的问题。看看实施情况:
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;
}
当asyc方法调用时,它实际上是同步执行的,直到第一次真正的异步操作。真正的异步操作是
wait\u emailSender.sendmailasync(messageToSend)
但只有在满足条件时才会调用它
if(exceptionMessages.Any()除外)
这意味着您的ExecuteAsync
方法将永远不会返回,因此StartAsync
。
Task.Delay
也是真正的异步方法(Thread.Sleep
不是),因此点击后,StartAsync
将继续并退出,您的第二个服务将有机会启动。首先不要在异步上下文中使用Thread.Sleep()
,因为它是阻塞操作。改用Task.Delay()
。我相信这是你的问题。看看实施情况:
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;
}
当asyc方法调用时,它实际上是同步执行的,直到第一次真正的异步操作。真正的异步操作是
wait\u emailSender.sendmailasync(messageToSend)
但只有在满足条件时才会调用它
if(exceptionMessages.Any()除外)
这意味着您的ExecuteAsync
方法将永远不会返回,因此StartAsync
。
Task.Delay
也是真正的异步方法(Thread.Sleep
不是),因此点击后StartAsync
将继续并退出,您的第二个服务将有机会启动。太好了!刚刚更新了我的代码并运行了几个测试。它似乎正在按其应有的方式行事。我觉得我需要更深入地研究异步处理,以便更好地理解内部逻辑。谢谢你的帮助@Artur!我还建议重写StarAsync
,并在那里调用InitializeDriver
,然后调用基本实现。通过这种方式,您将对象创建与初始化分离,调试将更容易。介意我再问一个问题吗?当满足“电子邮件服务”中的某个条件时,有什么方法可以同时优雅地结束所有正在运行的托管进程?您的ExecuteAsync
接收CancelationToken
-此令牌在服务停止时被取消。要触发服务停止,可以使用“iaapplicationlifetime.stopplication()”。但是请注意,它将停止整个web服务。在我的场景中,这是一个更好的结果:)谢谢!我尝试使用环境。退出(0)
,但它挂起了应用程序。。可能是因为从非主线程调用它。。使用iaapplicationlifetime.stopplication()
应用程序按预期退出。太好了!刚刚更新了我的代码并运行了几个测试。它似乎正在按其应有的方式行事。我觉得我需要更深入地研究异步处理,以便更好地理解内部逻辑。谢谢你的帮助@Artur!我还建议重写StarAsync
,并在那里调用InitializeDriver
,然后调用基本实现。通过这种方式,您将对象创建与初始化分离,调试将更容易。介意我再问一个问题吗?当满足“电子邮件服务”中的某个条件时,有什么方法可以同时优雅地结束所有运行的托管进程?您的ExecuteAsync
接收Cancelati
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;
}