Уничтожить поток событий

#c# #winforms #multithreading #events

#c# #винформы #многопоточность #Мероприятия #winforms

Вопрос:

У меня есть событие таймера, которое срабатывает каждую секунду. Иногда, когда я выхожу из программы (в отладчике VS), она сообщает мне, что поток событий пытается получить доступ к объекту, который больше не существует (потому что основной поток завершен). Я попытался отключить событие перед выходом ( UpdateTime.aTimer.Enabled = false; ). Это сократило количество случаев возникновения этой проблемы, но это все еще иногда случается, потому что событие срабатывает до того, как я могу его отключить.

  1. Это серьезная проблема? Будет ли это преследовать меня, если я не разберусь с этим?
  2. Если да, то как мне это сделать?

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

РЕДАКТИРОВАТЬ: Больше контекста. Это Winform.

Кроме того, я явно не создаю поток. Насколько я понимаю, поток автоматически создается для обработки событий.

Создание таймера:

 public static void Update(){

    System.Timers.Timer aTimer = new System.Timers.Timer(1000);
    aTimer.Elapsed  = new ElapsedEventHandler(OnTimedEvent);
    aTimer.Enabled = true;

}
  

Обработчик событий:

 private static void OnTimedEvent(object source,ElapsedEventArgs e) {

    Form1obj.updateLabel(String.Format("{0}", DateTime.Now.Second),Label1);

}
  

Закрытие обработчика программы:

 private void Form1_FormClosing(object sender,FormClosingEventArgs e) {

    aTimer.Enabled = false;

}
  

Ответ №1:

Серьезная проблема? Вероятно, нет, но я не думаю, что проблема должна быть серьезной, чтобы нуждаться в устранении. Предупреждения следует рассматривать, например, как ошибки при компиляции. Кроме того, если это приложение, отправленное клиентам, показывать уродливые ошибки при завершении работы не очень профессионально.

Как решить эту проблему, зависит от того, как вы настраиваете потоки («поток событий» не дает достаточной информации о механике). Одним из простых методов может быть приостановка основного потока на некоторое время, когда приложение завершает работу, пока все потоки не вернутся. Вы также должны прекратить выдачу новых потоков в течение этого времени.

Другим возможным решением было бы управлять тем, как создаются потоки, чтобы вы могли завершить процесс, который их запускает. На ум приходят такие концепции, как ThreadPool. Также в некоторых ситуациях может помочь явное подтверждение того, что потоки являются фоновыми.

Короткий ответ таков: никто не сможет дать вам четкий ответ «это решит вашу проблему» без какого-либо контекста того, что вы делаете в своем коде.

ДОБАВЛЕНО:

Есть несколько «быстрых» способов справиться с этим. У меня нет времени на полный анализ, поэтому посмотрите, работают ли они.

  1. Просто исправьте ошибку, дождавшись
  2. Добавьте счетчик и подождите, пока значение не уменьшится

Первое, что я бы рассмотрел, это добавление системы безопасности, позволяющей не обновлять метку в состоянии завершения работы. Это независимо от чего-либо еще, поскольку именно здесь возникают ваши ошибки. Я не думаю, что «основной поток отсутствует» является ядром проблемы, а скорее эта строка:

 Form1obj.updateLabel(String.Format("{0}", DateTime.Now.Second),Label1); 
  

Как вы можете обновить то, что больше не существует? Да, технически это происходит в основном потоке …

Простое ожидание было бы чем-то вроде:

 private void Form1_FormClosing(object sender,FormClosingEventArgs e) 
{
     aTimer.Enabled = false;  
     Thread.Sleep(5000);
} 
  

Скрывать форму тоже неплохая идея, чтобы пользователь этого не видел?

Если вы хотите использовать более «COM-подобный подход», вы можете добавить счетчик. Увеличивайте при обновлении () (при запуске события) и уменьшайте при OnTimedEvent(). Убедитесь, что вы блокируете счетчик при его изменении, чтобы в конечном итоге два потока не меняли его в одну и ту же миллисекунду. Затем вы можете подождать, пока счетчик не станет равным 0, чтобы завершить закрытие формы или выгрузку приложения.

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

Ответ №2:

Вы можете закрыть окно, как предложено в MSDN — когда вы устанавливаете таймер на отключение во время завершения обработки, установите флаг, который ваш Elapsed обработчик событий может проверить, чтобы знать, что больше никакой работы не требуется.

Истекшие события могут произойти после вызова метода Dispose или Stop или после того, как для свойства Enabled было установлено значение false, поскольку сигнал для вызова истекшего события всегда помещается в очередь для выполнения в потоке пула потоков. Один из способов разрешить это условие гонки — установить флаг, который сообщает обработчику событий для прошедшего события игнорировать последующие события.

Ответ №3:

Сложно дать общий ответ на вопрос, серьезно это или нет, это полностью зависит от того, что делает таймер. что это за таймер? система.Потоковая передача одного или одного из таймеров пользовательского интерфейса?

Если возможно, попробуйте реорганизовать свой код, чтобы вы могли сообщить таймеру прекратить запуск, хотя бы для того, чтобы не сбивать пользователей с толку сообщением об ошибке. это может быть так же просто, как совместное использование переменной или (предпочтительно) использование CancellationToken