Как завершить Task.Run?
Делаю WebApi. Команда Execute записывает запускает какое либо действие в цикле. Команда StopTaskById отменяет эту задачу.
Перепробовал множество вариантов из интернета. Задача либо не отменяется, либо пробрасывается ошибка, либо задача не останавливается.
Что делаю не так?
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using WebServer.Database;
using WebServer.Enums;
using WebServer.Extensions;
using WebServer.Models.ServiceParameters;
using WebServer.Utils;
namespace WebServer.Services;
[ApiController]
[Tags("[SERVICE]")]
[Route("api/[controller]")]
public class MnemonicCheckerService : AppResponse
{
private static CancellationTokenSource _cancellationTokenSource;
private CancellationToken _cancellationToken;
[HttpPost]
[Route("[action]")]
public async Task<JsonResult> Execute([FromBody] MnemonicCheckerParams mnemonicCheckerParams)
{
try
{
//Создаем токен отмены
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
//Создаем задачу
_tasker = Task.Run(async () =>
{
//Задача выполняет какое то действие в цикле
try
{
while (true)
{
await Task.Delay(1000);
Debug.WriteLine("AFTER" + new Random().Next(1000));
}
}
catch
{
//Если какая то ошибка записываем
}
}, _cancellationTokenSource.Token);
//Отправляет на клиента сообщение, что задача успешно запущена
return Json(true,$"The task has been successfully created! ID: {_task.Id} GUID: {_task.TaskGuidId}");
}
catch (Exception ex)
{
return Json(false, ex.Message);
}
}
[HttpGet]
[Route("[action]/{id}")]
public async Task<JsonResult> StopTaskById(int id)
{
//Останавливаем задачу и уведомляем пользователя, что задача успешно остановлена
try
{
_cancellationTokenSource.Cancel(false);
return Json(true,
$"Task successfully interrupted ID: {_task.Id} GUID: {_task.TaskGuidId} SERVICE: {_task.Service}");
}
catch (Exception ex)
{
_cancellationTokenSource.Dispose();
return Json(false, ex.Message);
}
}
}
Ответы (1 шт):
Автор решения: versetty777
→ Ссылка
Добавь проверку состояния токена в цикл while
while (!_cancellationToken.IsCancellationRequested)
{
await Task.Delay(1000);
Debug.WriteLine("AFTER" + new Random().Next(1000));
}
Добавь обработку исключения OperationCanceledException
catch (OperationCanceledException)
{
// Задача была отменена
}
Если ваша задача завершилась нормально, то не надо вызывать Dispose, он вызовется автоматом при завершении задачи и объект сам разрушится.
Кроме это можно взять класс System.Threading.Timer вместо задачи
using System;
using System.Threading;
public class TimerExample
{
private static Timer _timer;
public static void Main()
{
_timer = new Timer(TimerCallback, null, 0, 1000);
// Пауза на 10 секунд, чтобы таймер успел выполниться несколько раз
Thread.Sleep(10000);
// Останавливаем таймер
_timer.Dispose();
}
private static void TimerCallback(Object o)
{
// Код, который должен выполняться в цикле
Console.WriteLine("TimerCallback: " + DateTime.Now);
}
}
Либо через Task.Wait() или Task.WaitAll() вместо цикла while(true)
_task = Task.Run(() =>
{
try
{
// do some work
Task.Delay(1000, _cancellationToken).Wait();
Debug.WriteLine("AFTER" + new Random().Next(1000));
}
catch (OperationCanceledException)
{
if (_cancellationToken.IsCancellationRequested)
{
// task was canceled
}
}
}, _cancellationToken);
Через StopTaskById:
_cancellationTokenSource.Cancel();
try
{
_task.Wait();
}
catch (AggregateException ex)
{
if (ex.InnerExceptions[0] is TaskCanceledException)
{
// task was canceled
}
}