我正在IHostedService
中尝试dotnet core 2.2
。我的任务是创建2个长期运行的后台任务。
Selenium
浏览器会话(打开/关闭选项卡,解析DOM)并将电子邮件放在ConcurrentBag
内部的队列中。ConcurrentBag
中存在的消息(已添加第一个任务)。还将它们分组在一起,以便仅发送1条消息。但是,我无法同时运行两个托管进程。似乎只有第一个托管进程正在执行,而第二个进程等待第一个完全执行。但是由于我从没想到它会完成-第二个过程永远不会开始...
我滥用IHostedService
吗?如果可以,那么完成任务的最佳架构方法是什么?
这是我当前正在使用的代码(尝试完成):
using System;
// ..
namespace WebPageMonitor
{
class Program
{
public static ConcurrentBag<string> Messages = new ConcurrentBag<string>();
static void Main(string[] args)
{
BuildWebHost(args)
.Run();
Console.ReadKey();
}
private static IHost BuildWebHost(string[] args)
{
var hostBuilder = new HostBuilder()
.ConfigureHostConfiguration(config =>
{
config.AddJsonFile("emailSettings.json", optional: true);
config.AddEnvironmentVariables();
})
.ConfigureServices((hostContext, services) =>
{
services.AddOptions();
var bindConfig = new EmailSettings();
hostContext.Configuration.GetSection("EmailSettings").Bind(bindConfig);
services.AddSingleton<EmailSettings>(bindConfig);
services.AddTransient<EmailSender>();
services.AddHostedService<BrowserWorkerHostedService>();
services.AddHostedService<EmailWorkerHostedService>();
});
return hostBuilder.Build();
}
}
}
BrowserWorkerHostedService
public class BrowserWorkerHostedService : BackgroundService
{
private static IWebDriver _driver;
public BrowserWorkerHostedService()
{
InitializeDriver();
}
private void InitializeDriver()
{
try
{
ChromeOptions options = new ChromeOptions();
options.AddArgument("start-maximized");
options.AddArgument("--disable-infobars");
options.AddArgument("no-sandbox");
_driver = new ChromeDriver(options);
}
catch (Exception ex)
{
Program.Messages.Add("Exception: " + ex.ToString());
Console.WriteLine($" Exception:{ex.ToString()}");
throw ex;
}
}
protected override async Task ExecuteAsync(CancellationToken stopToken)
{
while (!stopToken.IsCancellationRequested)
{
try
{
_driver.Navigate().GoToUrl("https://www.google.com");
Program.Messages.Add("Successfully opened a website!");
// rest of the processing here
Thread.Sleep(60_000);
}
catch (Exception ex)
{
Program.Messages.Add("Exception: " + ex.ToString());
Console.WriteLine(ex.ToString());
Thread.Sleep(120_000);
}
}
_driver?.Quit();
_driver?.Dispose();
}
}
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);
}
}
}
编辑:应用答案中建议的更改后,这是当前可用的代码版本。添加await
很有帮助。
答案 0 :(得分:2)
首先不要在异步上下文中使用Thread.Sleep()
,因为它会阻止操作。请改用Task.Delay()
。我相信这是您的问题。查看BackgroundService.StartAsync的实现:
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方法时,它会同步执行,直到执行第一个真正的异步操作为止。您真正的异步操作是
等待_emailSender.SendEmailAsync(messageToSend);
但是只有在满足条件时才会被调用
如果(exceptionMessages.Any())
这意味着您的ExecuteAsync
方法将永远不会返回,所以StartAsync
也是如此。
Task.Delay
也是真正的异步方法(不是Thread.Sleep
),因此在被点击后,StartAsync
将继续并退出,您的第二项服务将有机会启动。