С# как моментально остановить поток?

Запускаю одновременно несколько тасков:

using (semaphore = new SemaphoreSlim(maxConcurrency))
{
    List<Task> tasks = new List<Task>();

    for (int i = 0; i < MyCollection.Count; i++)
    {
        int id = i;
        await semaphore.WaitAsync();  
                    
        tasks.Add(Task.Run(() =>
        {
              progress.Report("Автопостинг, канал : " + id);
        
              AddPost(progress, id, semaphore, false);
         }));

     }

     StatusWork = true;

      await Task.WhenAll(tasks);
}

Метод AddPost() выглядит примерно вот так:

 public async void AddPost(IProgress<string> progress, int ChanelNubmer, SemaphoreSlim semaphore, bool IndividualStartCheck)
    {
        for (; ; )
        {
            // Полезная нагрузка 
            Task.Delay(200000);

            if (Stop == true)
            {
                progress.Report("Поток " + ChanelNubmer + " остановлен!");
                MyCollection[ChanelNubmer].Status = "Не работает";
                return;
            }
        }
    }

Метод выполняет определенную работу, затем ждет 200000 миллисекунд и повторяет действие заново.

Если нужно остановить работу метода - нажатием кнопки на форме я присваиваю логической переменной Stop значение true

Проблема в том что используя Task.Delay(200000); - цикл будет остановлен не сразу.

Как в данном случае можно остановить работу метода моментально?

И метод AddPost я запускаю одновременно в нескольких потоках, если присвоить Stop == true - остановятся все потоки, можно как то обратиться к определенному потоку что бы остановить только его?


Ответы (1 шт):

Автор решения: aepot

Используйте CancellationToken.

async void нельзя ожидать, поэтому вся ваша конструкция с семафором не имела никакого смысла.

Асинхронное программирование - прочитайте обязательно.

public async Task AddPost(IProgress<string> progress, int channelNumber, SemaphoreSlim semaphore, bool individualStartCheck, CancellationToken token)
{
    progress.Report("Автопостинг, канал : " + channelNumber);
    try
    {
        for (;;)
        {
            // Полезная нагрузка 
            await Task.Delay(200000, token);
        }
    }
    catch (OperationCanceledException)
    { }
    finally
    {
        semaphore.Release();
    }
    progress.Report("Поток " + channelNumber + " остановлен!");
    MyCollection[channelNumber].Status = "Не работает";
}

Далее объявите CancellationTokenSource

private CancellationTokenSource _cts;

И вот так это использовать

if (_cts != null)
    return;

using (_cts = new CancellationTokenSource())
using (SemaphoreSlim semaphore = new SemaphoreSlim(maxConcurrency))
{
    List<Task> tasks = new List<Task>();
    try
    {
        for (int i = 0; i < MyCollection.Count; i++)
        {
            await semaphore.WaitAsync(_cts.Token);  
                    
            tasks.Add(AddPost(progress, i, semaphore, false, _cts.Token));
        }
    }
    catch (OperationCanceledException)
    { }
    await Task.WhenAll(tasks);
}
_cts = null;

Чтобы всё остановить, просто вызовите

_cts?.Cancel();

При срабатывании отмены, асинхронные методы .NET бросают исключение OperationCanceledException, его нужно всего-лишь поймать. При желании вы так же внутри своих методов можете его бросить вот так:

token.ThrowIfCancellationRequested();

Или без выбрасывания исключения делать проверку типа

if (token.IsCancellationRequested)
    return;
→ Ссылка