Как перезапустить задачу в С#? или Как перезапустить Thread.Sleep()?

Я пытался создать приложение для Windows, в котором, когда я получаю сообщение «some_string» (с сервера), мне нужно изменить цвет метки (sys2lbl в коде) на зеленый, и он должен оставаться зеленым в течение 15 секунд, а затем покраснеть. Однако, например, в течение 5-й секунды, если я снова получаю сообщение 'some_string', метка должна оставаться зеленой еще 15 секунд, т.е. всего 5+15 = 20.

Я использую Task.Run(), насколько я понимаю, эта задача должна быть как-то перезапущена, когда я получу строку 'some_string' в течение этих 15 секунд.

Примечание. Эта задача выполняется отлично, если я получаю «some_string» через 15 секунд.

Кто-нибудь знает, как это решить?

Task.Run(() => 
{
    if (x.ApplicationMessage.Topic == "some_string")
    { 
        sys2lbl.BackColor = Color.Green;
        Thread.Sleep(15000);
        sys2lbl.BackColor = Color.Red;
    }
});

person Manoj_S    schedule 10.06.2021    source источник
comment
Мое понимание Thread.Sleep() заключается в том, что он буквально спит поток, с которым вы взаимодействуете. Вы не можете делать что-либо еще с этим потоком, пока не истечет срок. Вот почему Thread.Sleep во многих случаях не обязательно является хорошей идеей. Похоже, вам может понадобиться поведение класса Timer: docs.microsoft.com/en-us/dotnet/api/   -  person Symon    schedule 10.06.2021
comment
Что на практике означает когда я получаю сообщение от сервера? Вы опрашиваете API или сервер подталкивает его? То есть, есть ли у вас что-то вроде цикла сообщений, который отправляет в поток пользовательского интерфейса? Или это управляется событиями? Асинхронный? -- Если вам нужно запустить фоновый поток для опроса сервера, вам нужен делегат, вызываемый в потоке пользовательского интерфейса. Здесь вы устанавливаете состояние элемента управления (не из другого потока/задачи). Вы можете использовать таймер в потоке пользовательского интерфейса и увеличить его Interval на 5 секунд, если он все еще активен, в противном случае просто запустите его. -- Если вам действительно нужен другой Thread.   -  person Jimi    schedule 10.06.2021
comment
Пользовательский интерфейс, который я делаю, подписывается на темы на сервере MQTT. Итак, сообщения публикуются на сервере случайным образом. Всякий раз, когда я подписываюсь на определенную тему, метка должна становиться зеленой и оставаться такой некоторое время, а затем становится красной.   -  person Manoj_S    schedule 10.06.2021
comment
Итак, подписываетесь ли вы на определенные темы, используя возможности публикации брокера? Затем вы должны получить новое сообщение через событие, которое возникает в потоке, отличном от потока пользовательского интерфейса. Вы можете использовать делегата, созданного в потоке пользовательского интерфейса, для маршалинга новых данных в поток пользовательского интерфейса. Вы можете либо использовать IProgrsss<T> делегат, либо Invoke()/BeginInvoke() свой собственный делегат (вам нужно проверить, является ли синхронная версия более надежной, чем асинхронная), когда событие получено. Делегат, как описано, либо запускает таймер, либо увеличивает его интервал.   -  person Jimi    schedule 10.06.2021
comment
Кстати, вместо того, чтобы помечать свой вопрос не относящимся к делу cancellationtoken или подобным (как если бы вы предлагали решение), почему бы вам не пометить его mqtt и не опубликовать код, который вы используете для получения уведомлений издателя. ? Является ли ваш клиент также издателем? То есть у вас могут быть некоторые состояния гонки, которые вам нужно решить.   -  person Jimi    schedule 10.06.2021
comment
Отвечает ли это на ваш вопрос? Перезапустить выполненную задачу и Как отменить и перезапустить задачу C#   -  person Olivier Rogier    schedule 10.06.2021


Ответы (1)


Вы можете использовать Timer из пространства имен System.Windows.Forms. В вашей форме объявите

private readonly Timer _timer = new Timer();

Инициализируйте это так

public Form1()
{
    InitializeComponent();

    _timer.Interval = 15000; // Milliseconds
    _timer.Tick += Timer_Tick;
}

private void Timer_Tick(object sender, EventArgs e)
{
    _timer.Stop();
    sys2lbl.BackColor = Color.Red;
}

Затем вы можете запустить или перезапустить таймер с помощью

private void RestartTimer()
{
    _timer.Stop();
    sys2lbl.BackColor = Color.Green;
    _timer.Start();
}

Я часто использую эту технику для задержки выполнения запроса. В качестве примера предположим, что у нас есть сетка данных и текстовое поле, используемое для фильтрации данных в сетке. В событии TextChanged текстового поля мы запускаем таймер с небольшой задержкой (например, 200 мс). В Timer_Tick мы останавливаем таймер и повторно запрашиваем данные.

Преимущество заключается в том, что данные повторно запрашиваются без необходимости нажатия Enter или кнопки. Но, с другой стороны, данные не запрашиваются повторно после каждого нажатия клавиши, что может быть проблематично, если выполнение запроса происходит медленно.

Если пользователь вводит следующий символ до истечения задержки таймера, таймер перезапускается. Если запрос выполняется при входе в фильтр из-за того, что пользователь печатает медленно, следующие нажатия клавиш будут накапливаться, а затем сразу вставляться в текстовое поле после завершения обновления данных. Это уменьшает количество повторных запросов и делает пользовательский интерфейс более отзывчивым.

Обратите внимание, что в Windows Forms события выполняются строго последовательно. Новое событие никогда не прерывает метод (который может быть обработчиком событий), работающий в потоке пользовательского интерфейса. Поэтому у вас никогда не будет проблем с многопоточностью с таймером.

person Olivier Jacot-Descombes    schedule 10.06.2021