Разрешить дочернему потоку вызывать делегат для родителя, когда родитель находится в состоянии соединения, ожидающего ребенка в C#
У меня есть форма, которая отвечает за создание и настройку экземпляра объекта, а затем говорит объекту идти делать свою работу. Процесс долгий, поэтому в форме есть область, где появляются сообщения о состоянии, позволяющие пользователю узнать, что происходит. Сообщения устанавливаются с помощью функции setMessage (string msg). Чтобы форма оставалась чувствительной к событиям, я создаю новый поток для запуска объекта и передаю ей функцию setMessage в качестве делегата, чтобы объект мог устанавливать сообщения о состоянии в форме. Эта часть работает правильно. Основная форма является отзывчивой, и сообщения, отправленные в ее функцию setMessage, отображаются, как и ожидалось.
Поскольку процесс длинный и состоит из множества шагов, я хочу позволить пользователю завершить процесс до его завершения. Для этого я создал изменяемый тип bool с именем _stopRequested и функцию shouldStop(), которая возвращает его значение. Это также дается объекту как делегат. Объект может определить, должен ли он завершиться, периодически проверяя mustStop () и, если это правда, корректно завершать работу.
Наконец, элементы управления Windows не являются потокобезопасными, поэтому компилятор будет жаловаться, если поток, отличный от того, который создал элемент управления, попытается манипулировать им. Поэтому функция setMessage обернута в оператор if, который проверяет это и вызывает функцию, используя родительский поток, если она вызывается из рабочего потока (см. http://msdn.microsoft.com/en-us/library/ms171728%28v=vs.80%29.aspx для описания).
Проблема возникает, когда пользователь запрашивает завершение работы. Основная форма устанавливает для _stopRequested значение true, а затем ожидает завершения дочернего потока перед закрытием приложения. Это делается путем выполнения _child.Join(). Теперь родительский поток (тот, на котором запущена форма) находится в состоянии Join и ничего не может сделать. Дочерний поток (выполняющий длинный процесс) обнаруживает флаг остановки и пытается завершить работу, но перед этим он отправляет сообщение о состоянии, вызывая его делегат setMessage. Этот делегат указывает на основную форму, которая выясняет, что поток, устанавливающий сообщение (дочерний элемент), отличается от потока, создавшего элемент управления (родительский элемент), и вызывает функцию в родительском потоке. Разумеется, родительский поток находится в состоянии Join и не будет устанавливать текст в текстовом поле, пока дочерний поток не завершится. Дочерний поток не завершается, потому что ожидает делегата, который он вызвал, для возврата. Мгновенный тупик.
Я нашел примеры сигнализации о завершении потока, и я нашел примеры дочерних потоков, отправляющих сообщения родительскому потоку, но я не могу найти примеров того, как обе вещи происходят одновременно. Может кто-нибудь дать мне несколько советов о том, как избежать этого тупика? В частности, я бы хотел, чтобы форма дожидалась завершения дочернего потока перед закрытием приложения, но оставалась способной выполнять работу, пока оно ожидает.
Заранее спасибо за совет.
1 ответ
1-(ленивый) Отправьте метод из нового потока, чтобы он не блокировался
2-(переосмыслить) Основной поток пользовательского интерфейса должен иметь возможность контролировать дочерний поток, поэтому забудьте _stopRequested и shouldStop() и реализуйте childThread.Abort(), прерывание не прерывает поток, но отправляет исключение ThreadAbortException, которое может быть обработанным или даже отмененным
catch(ThreadAbortException e)
{
ReleaseResources();
}
Сделайте ReleaseResources безопасным, сделав различные проверки, такие как:
resource != null
или же
resource.IsClosed()
ReleaseResources должен вызываться как обычно без прерывания, а также путем прерывания.
3- (если возможно) остановить дочерний процесс через вызов основного потока ReleaseResources ()
Возможно, вам придется реализовать сочетание этих.