Отменить, если та же задача вызывается Task.Run

У меня есть код, как показано ниже. Просто запустите цикл while на долгое время.

public void Test(){
    var task1 = Task.Run(() =>
    {
        while(true){
            // forever
        }
    }
}

Когда Test() вызывается, task1 будет выполнено. Затем, когда я снова запускаю Test(), одновременно существуют две задачи.

Есть ли способ отменить задачу, которая уже выполнялась, при создании новой задачи?

Редактировать для Михала Турчина

    int i = 0;
    private CancellationTokenSource cancellationTokenSource;

    public void Test(int b)
    {
        if (cancellationTokenSource != null)
        {
            // Cancel previously created task.
            cancellationTokenSource.Cancel();
        }

        cancellationTokenSource = new CancellationTokenSource();

        var task1 = Task.Run(() =>
        {
            while (!cancellationTokenSource.IsCancellationRequested)
            {
                Console.WriteLine(b);
                // forever
            }
        });
    }

    public void button_pushed(){
        Console.WriteLine("pushed");
        Test(i); i++; return;
    }

Лог такой:

pushed
0
0
0
0
0
pushed
1
1
1
1
pushed
1
1
1
1
2
2
2
2
2
1
1
1
1
2
2
2
pushed
1
1
1
1
2
2
2
2
2
1
1
1
1
3
3
3
3
2
2
2

....

Похоже, прошлые задачи все еще выполняются. Но когда я выполнил это три раза, первый вывод (0) исчез, так это проблема времени?

То, что я ищу, похоже на:

pushed
0
0
0
0
pushed
1
1
1
1
pushed
2
2
2
2

....

Почему бы не использовать task1 в качестве поля/свойства вместо локальной переменной?

Dmitry Bychenko 22.08.2024 12:00

Передайте CancellationToken методу, затем измените его на while (!CancellationToken.IsCancelationRequested).

Poul Bak 22.08.2024 12:01

Вы действительно хотите отменить первый запуск или предпочитаете, чтобы второй вызов не запускал второй? Как есть, кажется произвольным, какой из двух работает, пока работает один.

Fildor 22.08.2024 12:03

У меня возникнет соблазн сделать шаг назад и спросить, хорошее ли это решение? Например, может быть, вам лучше использовать BackgroundService?

DavidG 22.08.2024 12:17
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете попробовать использовать CancellationTokenSource. когда метод вызывается (и Task создается, передаем ему токен для CancellationTokenSource, который также будет создан для задачи. Перед созданием CancellationTokenSource нам просто нужно проверить, был ли он создан, и если да, вызвать метод Cancel.

Простая реализация:

private CancellationTokenSource cancellationTokenSource;

public void Test()
{
    if (cancellationTokenSource != null)
    {
        // Cancel previously created task.
        cancellationTokenSource.Cancel();
    }

    cancellationTokenSource = new();

    var task1 = Task.Run(() =>
    {
        while(!cancellationTokenSource.IsCancellationRequested){
            // forever
        }
    });
}

Конечно, если вы хотите, вы можете отслеживать все CancellationTokenSource в некоторой коллекции, сделать ее потокобезопасной, используя ConcurrentBag для их хранения и т. д. Это просто упрощенная идея.

ОБНОВЛЯТЬ

Прочитав ваше обновление с моим решением, я решил дать более подробную реализацию с сохранением всех источников токенов отмены и их правильной отменой:

public class Test
{
    int i = 0;
    private ConcurrentBag<CancellationTokenSource> cancellationTokenSources = new();

    public void Test(int b)
    {
        var notCancelled = cancellationTokenSources
            .Where(c => !c.IsCancellationRequested)
            .ToArray();
        // Cancel previously created tasks.
        Array.ForEach(notCancelled, x => x.Cancel());

        var cts = new CancellationTokenSource();
        cancellationTokenSources.Add(cts);

        var task1 = Task.Run(() =>
        {
            while (!cts.IsCancellationRequested)
            {
                Console.WriteLine(b);
                // forever
            }
            Console.WriteLine("FININSHED");
        });
    }

    public void button_pushed()
    {
        Console.WriteLine("pushed");
        Test(i); i++; return;
    }
}

Передача Token задаче только отменяет ее, если она еще не началась, поэтому она не работает с Task.Run, поскольку запускается немедленно. Вы должны проверить Token вручную.

Poul Bak 22.08.2024 12:21
while(true){ следует проверить состояние токена, верно? (Не ДВ)
Fildor 22.08.2024 12:21

@PoulBak Спасибо, что указали на это

Michał Turczyn 22.08.2024 12:32

Чтобы уточнить: передача Tokento Task.Run работает только в том случае, если токен уже отменен, когда вы вызываете Task.Run. В этом случае Task никогда не запустится.

Poul Bak 22.08.2024 12:43

Спасибо за ваш ответ. Я попробовал ваш код, но кажется, что прошлые задачи все еще выполняются. Я отредактировал вопрос и включил более подробную информацию. Я был бы рад, если бы вы могли научить меня, когда у вас будет время.

yolo 22.08.2024 13:47

Другие вопросы по теме