Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 同时启动多个长期运行的后台服务_C#_.net Core_Background Process_Asp.net Core Hosted Services - Fatal编程技术网

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
。我的任务是创建两个后台长时间运行的任务

  • 第一个是管理
    Selenium
    浏览器会话(打开/关闭选项卡,解析DOM),并将电子邮件消息放入
    ConcurrentBag
    内的队列中
  • 第二个后台工作人员每10分钟发送一次电子邮件通知,通知存在于
    ConcurrentBag
    中的消息(第一个任务添加了该消息)。它还将它们分组,以便只发送一条消息
但是,我在同时运行2个托管进程时遇到问题。似乎只有第一个托管进程被执行,而第二个进程等待第一个进程被完全执行。但是,因为我从来没有期望它会完成-第二个过程从来没有开始

我是否误用了
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;
    }