2011-01-04 6 views
1

У меня есть два фоновых потока и поток для обработки минимизации, выполняющийся в моем приложении WinForms. Когда программа закрывается, я использую этот метод.C# Добавление кода в событие FormClosing отключает X на MdiParent

private void MyForm_Closing(object sender, FormClosingEventArgs e) 
    { 
     if(labelsUpdaterThread.IsAlive == true) 
      labelsUpdaterThread.Abort(); 
     if(printNotifyThread.IsAlive == true) 
      printNotifyThread.Abort(); 
     if(minimizeThread.IsAlive == true) 
      minimizeThread.Abort(); 
    } 

labelsUpdaterThread and printNotifyThread все время работает. MinimizeThread, как вы могли догадаться, работает только при свертывании родительской формы. Вот моя проблема:

Когда методы thread.abort вызываются в моем методе выше, «X» в верхнем правом углу моей формы MdiParent ничего не делает. Щелчок по нему не влияет.

Когда методы thread.abort НЕ вызывается в моем методе выше, закрытие MdiParent иногда вызывает исключения, поскольку потоки все еще пытаются получить доступ к ресурсам на MdiParent, которые больше не доступны, хотя они являются фоновыми потоками!

Я не уверен, почему это происходит, не имеет для меня никакого смысла. Заранее благодарю за любую помощь!

+1

Если вы не делаете никаких проверок, которые могли бы отменить событие закрытия, вы должны использовать 'FormClosed' вместо' FormClosing'. – unholysampler

+0

Пожалуйста, не редактируйте образец кода в вопросе. Это может привести к тому, что ответы, приведенные ранее, выглядят некорректно. –

+0

@unholy: FormClosed происходит после закрытия формы. Мое приложение затем генерирует исключения, потому что они пытаются получить доступ к ресурсам в закрытом потоке. – CODe

ответ

4

Я согласен с Paul Alexander's answer в том, что вы должны никогда вызова Thread.Abort, это ужасный способ обработки синхронизации.

Кроме того, здесь есть ужасное разделение. Нити не должны напрямую обращаться к ресурсам в форме. Должно быть какое-то абстракционное/разделяемое состояние между ними, которое изменяется и считывается обеими сторонами (поток и форма, поэтому убедитесь, что экземпляр потокобезопасен).

Если вы не смогли внести эти изменения, то в методе Close вызовите методы Thread.Abort в другом потоке с инструкцией try/catch вокруг каждого. Запишите ошибку (-ы) где-то как минимум.

При выполнении вызовов Thread.Abort в другом потоке вы не блокируете поток пользовательского интерфейса, так как вызовы Thread.Abort не гарантируются мгновенно, и блокирование потока пользовательского интерфейса приведет к тому, что X будет выделен серым цветом, Поток пользовательского интерфейса не может обрабатывать сообщения Windows (это также помогает вам лучше разобраться в проблемах).

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

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

Оттуда вы можете ввести надлежащий механизм отмены сотрудничества (cooperative cancellation, which Task supports in .NET 4.0, если это доступно для вас).

+0

Простите мое отсутствие знаний, как бы назвал Thread.Прервать из другой темы с помощью try/catchs мой случай? Чем он отличается от окружающего, что у меня есть с помощью try/catch? Большое спасибо за вашу помощь. – CODe

+1

@CODe: при вызове 'Thread.Abort' в другом потоке вы не блокируете свой поток пользовательского интерфейса, который является причиной вашей проблемы в первую очередь (X отключен). Я обновил свой ответ, чтобы отразить это. – casperOne

+0

Спасибо! Новый поток работал. Пока я доволен этим быстрым решением, но я буду настаивать на абстракции, о которой вы говорили в ближайшие дни. Мой работодатель только позволяет мне работать определенное количество часов каждый день, к сожалению. ;) Я очень ценю ваш вклад! – CODe

0

Вызовы Abort, вероятно, бросают исключения. Убедитесь, что указатели действительны, и потоки остаются действительными (не удаляются), прежде чем вызывать прерывание.

И, в визуальной студии, откройте Debug \ Exceptions ... и установите проверку в столбце 'throw' для всех исключений, чтобы вы видели, что что-то пошло не так.

+0

Изменил Исключения, он выбрал это исключение на первом потоке. »Пользовательский код, запущенный в потоке 5736 попытался прервать поток 5692. Это может привести к повреждению или утечке ресурсов, если поток, который прерывается, находится в середине операции, которая изменяет глобальное состояние или использует собственные ресурсы. Отмена потоков, отличных от текущего выполняемого потока, сильно обескуражена. " – CODe

+0

Итак, каково правильное поведение для закрытия приложения WinForms при запуске фоновых потоков? – CODe

+0

@CODe: более безопасным способом было бы установить флаг, который приложение закрывает, убедитесь, что все фоновые потоки проверяют его регулярно и если они видят, что он установлен. Затем выполните 'Join()' в потоке, чтобы дождаться его закрытия. Я предпочитаю «BackgroundWorkers» при запуске потоков в Приложения с графическим интерфейсом, но это еще больше. –

0

Сначала удалите вызовы на .Abort() и никогда не используйте их снова. Нити никогда не должны прерываться, вызывая Abort. В основном это приводит к сбою вашего потока и не дает ему возможности выпустить любые ресурсы должным образом или освободить любые системные дескрипторы. Вместо этого создайте ManualResetEvent и проверьте это в своих потоках. Когда событие установлено, они должны прекратиться.

Резьба1

while(! _stopEvent.WaitOne(0)) 
{ 
    ...do my thready work 

} 

Тогда при закрытии

private void MyForm_Closing(object sender, FormClosingEventArgs e) 
{ 
    _stopEvent.Set(); 
    labelsUpdaterThread.Join(); 
    ... 
} 

Если вы не заботитесь, если потоки прекратить должным образом на применении существуют, просто установите IsBackground = true, и они будут автоматически прекращается, когда приложение выходы.

+0

Выполнение вышеуказанного и нажатие на X теперь приводит к блокировке всего моего приложения, не выбрасывая исключение, как это делалось ранее. Он просто замерзает. :/ – CODe

+0

Он заблокирует ваше приложение, если ваши потоки не будут правильно отслеживать _stopEvent. Они должны периодически проводить опрос, чтобы узнать, установлено ли событие, и если да, вернитесь из функции основного потока. Возможно, вы захотите ознакомиться с многопоточным программированием, чтобы лучше понять последствия. Параллельное программирование в Windows - отличный ресурс (http://www.amazon.com/gp/product/032143482X?ie=UTF8&tag=xi00-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=032143482X). –

+0

Кроме того, установка IsBackground = true для ваших потоков позволит .NET прекратить их автоматически при выходе из приложения - не заблокирует ваше приложение. –