Отмена асинхронного метода, который вызывает события
У меня есть безголовое приложение UWP, которое использует внешнюю библиотеку для подключения к последовательному устройству и отправки некоторых команд. Он запускает бесконечный цикл (в то время как истина) с 10-минутной паузой между циклами. Процесс измерения занимает около 4 минут. Внешняя библиотека должна выполнить 3 измерения, и после каждого она сигнализирует о возникновении события. Когда событие поднимается в четвертый раз, я знаю, что могу вернуть результаты.
Через 4 часа (+/- несколько секунд) библиотека перестает вызывать события (обычно она вызывает событие один или два раза, а затем останавливается, ошибок нет, ничего).
Я реализовал в DoMeasureAsync() ниже CancellationTokenSource, который должен был установить свойство IsCancelled для TaskCompletionSource через 8 минут, чтобы задача возвращалась и цикл продолжался.
Проблема: Когда измерение не завершается (NMeasureCompletionSource никогда не получает свой набор результатов в классе CMeasure), задача из nMeasureCompletionSource никогда не отменяется. Делегат, определенный в RespondToCancellationAsync(), должен быть запущен через 8 минут.
Если измерение проходит нормально, я вижу в журналах, что код в
taskAtHand.ContinueWith((x) =>
{
Logger.LogDebug("Disposing CancellationTokenSource...");
cancellationTokenSource.Dispose();
});
вызывается.
Изменить:Возможно ли, что GC входит через 4 часа и, возможно, освобождает некоторые переменные, и это делает приложение не в состоянии отправлять команды на датчик? - Дело не в этом
Что мне здесь не хватает?
//this gets called in a while (true) loop
public Task<PMeasurement> DoMeasureAsync()
{
nMeasureCompletionSource = new TaskCompletionSource<PMeasurement>();
cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(8));
var t = cMeasure.Run(nitrateMeasureCompletionSource, cancellationTokenSource.Token);
var taskAtHand = nitrateMeasureCompletionSource.Task;
taskAtHand.ContinueWith((x) =>
{
Logger.LogDebug("Disposing CancellationTokenSource...");
cancellationTokenSource.Dispose();
});
return taskAtHand;
}
public class CMeasure
{
public async Task Run(TaskCompletionSource<PMeasurement> tcs, CancellationToken cancellationToken)
{
try
{
NMeasureCompletionSource = tcs;
CancellationToken = cancellationToken;
CancellationToken.Register(async () => await RespondToCancellationAsync(), useSynchronizationContext: false);
CloseDevice(); //Closing device if for some reason is still open
await Task.Delay(2500);
TheDevice = await GetDevice();
measurementsdone = 0;
Process(); //start the first measurement
}
catch (Exception ex)
{
DisconnectCommManagerAndCloseDevice();
NMeasureCompletionSource.SetException(ex);
}
}
public async Task RespondToCancellationAsync()
{
if (!NitrateMeasureCompletionSource.Task.IsCompleted)
{
Logger.LogDebug("Measure Completion Source is not completed. Cancelling...");
NMeasureCompletionSource.SetCanceled();
}
DisconnectCommManagerAndCloseDevice();
await Task.Delay(2500);
}
private void Process()
{
if (measurementsdone < 3)
{
var message = Comm.Measure(m); //start a new measurement on the device
}
else
{
...
NMeasureCompletionSource.SetResult(result);
}
}
//the method called when the event is raised by the external library
private void Comm_EndMeasurement(object sender, EventArgs e)
{
measurementsdone++;
Process();
}
}
1 ответ
После дополнительных испытаний я пришел к выводу, что утечки памяти нет и что все объекты уничтожены. Отмена работает также хорошо.
Пока что кажется, что моя проблема связана с запуском приложения без головы на Raspberry Pi. Хотя я использую deferral = taskInstance.GetDeferral(); кажется, что выполнение остановлено в некоторый момент...
Я протестирую больше и вернусь с результатами (возможно, в новом посте, но я также поставлю здесь ссылку).
Изменить: вот новое сообщение: UWP - приложение без головы останавливается через 3 или 4 часа
Изменить 2: Проблема была из сторонней библиотеки, которую я должен был использовать, и она должна была вызываться не так, как приложение без наушников. Внутренне он создавал свой собственный TaskScheduler, если SynchronizationContext.Current был нулевым.