#c# #asp.net #.net #multithreading #task-parallel-library
#c# #asp.net #.net #многопоточность #задача-параллельная-библиотека
Вопрос:
Я использую Task.Run внутри backgroundservice. У меня есть backgroundservice, который отвечает за вход в систему и отправку сердцебиения для конкретной системы.
public class TestBGService : IHostedService
{
private readonly ITestService _testService;
private bool MustLogOn { get; set; } = true;
private string id { get; set; }
public TestBGService(ITestService testService)
{
_testService = testService;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
Log.Information("TestBGService starts Initialize");
await Initialize();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private async Task Initialize()
{
try
{
if (MustLogOn)
{
Log.Information("TestBGService ExecuteAsync trying to LogOnAsync");
id = await _testService.LogOnAsync();
if (!string.IsNullOrEmpty(id))
{
Log.Information($"new id equals to {id}");
MustLogOn = false;
_ = Task.Run(async () =>
{
while (true)
{
bool res = await SendHeartBeat(id);
Log.Information($"res from SendHeartBeat {res}");
if (!res)
{
break;
}
await Task.Delay(10000);
}
});
await _testService.StartProcessAsync(id);
}
}
}
catch (Exception ex)
{
Log.Error($"TestBGService ExecuteAsync throws {ex.ToString()}");
}
}
private async Task<bool> SendHeartBeat(string id)
{
bool isSuccess = true;
try
{
Log.Information("TestBGService sending heartbeat at " DateTime.Now);
var response = new HeartBeatResponseModel();
response = await _testService.SendHeartBeatAsync(id);
Log.Information("TestBGService heartbeat response equals to " response.IsSuccessful);
if (!response.IsSuccessful)
{
MustLogOn = true;
isSuccess = response.IsSuccessful;
}
}
catch (Exception ex)
{
Log.Error(ex, "TestBGService SendHeartBeat throws");
isSuccess = false;
MustLogOn = true;
}
return isSuccess;
}
}
Инициализируйте метод, пытающийся войти в систему, и в случае успеха он должен начать отправлять Heartbeat. Метод SendHeartBeat отвечает за получение успеха или сбоя. В случае успеха я изменяю значение MustLogon на false и отправляю SendHeartBeat каждые 10 секунд. Параллельно для SendHeartBeat мне нужно позвонить _testService.StartProcessAsync
, чтобы получить данные из потока. Каким-то образом он перестает работать и снова начинает вход в систему, но мне нужно, чтобы до тех пор, пока он не вернет false, он должен работать, и SendHeartBeat необходимо выполнять каждые 10 секунд, но, к сожалению, он перестает работать в случае bool res = true
и в этот момент он не выдает никаких исключений. Есть предложения?
Комментарии:
1. Взгляните на этот пример: learn.microsoft.com/en-us/aspnet/core/fundamentals/host / … и попробуйте соответствующим образом изменить свой код. Вы не должны вечно зависать в методе startAsync.
2. Например, он останавливается, когда я отправляю CancellationToken
3. Да, потому что это мусор (извините). Пожалуйста, посмотрите, как должна работать служба синхронизации (см. Ссылку в первом комментарии). В обратном вызове таймера: 1. Войдите в систему, если необходимо, 2. Отправьте Heartbeat. Выполнено. Не запускайте задачи изнутри задач…
4. Или вы можете просто быстро выполнить грязное использование
BackgroundService
по предоставленной ссылке @Fildor, переместив свою »Initialize
» логику вExecuteAsync
(и удаливTask.Run
, какой в этом смысл здесь?).5. Буквально ничто не мешает вам сделать это.
Ответ №1:
Это то, что я придумал, перейдя по ссылке из базового класса BackgroundService и «подключив» необходимую функциональность: (непроверенный, конечно)
public class TestBGService : BackGroundService
{
// I guess you are using different logging
private readonly ILogger<TestBGService> _logger;
// Your service to send Heartbeats to
private readonly ITestService _testService;
// If id is null, we need to login.
private string _id = null;
public TestBGService (ILogger<TestBGService> logger, ITestService testService)
{
_logger = logger;
_testService = testService ?? throw new ArgumentNullException(nameof(testService));
}
public Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("TestBGService running.");
return Task.CompletedTask;
}
// This will be triggered by the runtime.
private async Task ExecuteAsync (System.Threading.CancellationToken stoppingToken)
{
while( !stoppingToken.CancellationRequested )
{
try
{
if( string.IsNullOrEmpty(_id) )
{
// _id is null => perform login
_id = await _testService.LogOnAsync(); // perhaps pass stoppingToken?
}
bool res = await SendHeartBeat(id); // again: consider passing stoppingToken
if( !res ) _id = null; // Heartbeat unsuccessful: login next time
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
}
catch(Exception ex) // You should actually catch more specific exceptions.
{
// TODO log Exception
_id = null; // Reset id, so we login next time.
}
}
}
private async Task<bool> SendHeartBeat(string id)
{
// Don't even try if login was unsuccessful.
if ( string.IsNullOrEmpty(id) ) return false;
_logger.LogInformation("TestBGService sending heartbeat at {0}" , DateTime.Now);
var response = await _testService.SendHeartBeatAsync(id);
_logger.LogInformation("TestBGService heartbeat response {0}successful", response.IsSuccessful ? "" : "un");
return response.IsSuccessful;
}
public Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("TestBGService is stopping.");
// TODO maybe explicit logout?
return Task.CompletedTask;
}
}