Реальность заключается в том, что с помощью Invoke и друзей вы не можете полностью защитить от вызова на удаленном компоненте, а затем получить InvalidOperationException из-за отсутствия дескриптора. Я havnt действительно видел ответ еще, как тот, который находится ниже, в любом из потоков, который затрагивает фундаментальную проблему, которая не может быть полностью решена путем выборочного тестирования или использования семантики блокировки.
Вот нормальная «правильная» идиома:
// the event handler. in this case preped for cross thread calls
void OnEventMyUpdate(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return; // ignore events if we arn't ready, and for
// invoke if cant listen to msg queue anyway
if (InvokeRequired)
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
else
this.MyUpdate(e.MyData);
}
// the update function
void MyUpdate(Object myData)
{
...
}
fundemental проблема:
При использовании объекта Invoke используется очередь окна сообщения, которое помещает сообщение в очередь либо ждать, либо запускать огонь и забывать о перекрестном потоке точно так же, как сообщение «Почта» или «Отправить».Если перед сообщением Invoke появляется сообщение, которое приведет к аннулированию компонента и его дескриптора окна или которое будет помещено сразу после любых проверок, которые вы пытаетесь выполнить, тогда у вас будет плохое время.
x thread -> PostMessage(WM_CLOSE); // put 'WM_CLOSE' in queue
y thread -> this.IsHandleCreated // yes we have a valid handle
y thread -> this.Invoke(); // put 'Invoke' in queue
ui thread -> this.Destroy(); // Close processed, handle gone
y thread -> throw Invalid....() // 'Send' comes back, thrown on calling thread y
Там нет реального способа узнать, что управление собирается удалить себя FROMTHE очереди, а на самом деле ничего разумного вы можете сделать, чтобы «отменить» ВЫЗОВ. Независимо от того, сколько проверок вы делаете или какие дополнительные блокировки вы делаете, вы не можете остановить кого-то другого, выдавая что-то вроде закрытия или деактивировать. Есть тонны сенариев, где это может произойти.
Решение:
Первое, что нужно понимать, что Invoke собирается на провал, не отличается от того, как (IsHandleCreated) чек проигнорировали бы это событие. Если целью является защита вызывающего абонента от потока, отличного от UI, вам нужно будет обрабатывать исключение и относиться к нему, как к любому другому вызову, который не удался (чтобы приложение не зависало или выполняло что угодно. И если вы не переписываете/перебросить Invoke объекта, улов ваш единственный способ узнать.
// the event handler. in this case preped for cross thread calls
void OnEventMyWhatever(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return;
if (InvokeRequired)
{
try
{
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
}
catch (InvalidOperationException ex) // pump died before we were processed
{
if (this.IsHandleCreated) throw; // not the droids we are looking for
}
}
else
{
this.MyUpdate(e.MyData);
}
}
// the update function
void MyUpdate(Object myData)
{
...
}
фильтрации исключение может быть адаптирована в соответствии с какими бы ни были потребности. Его хорошо, чтобы быть в курсе, что рабочие потоки часто не имеют все тепленькое внешнюю обработку исключений и протоколирование потоков пользовательского интерфейса выполняется в большинстве приложений, поэтому вы можете просто сожрать любое исключение на стороне рабочего. Или запишите и реконструируйте все из них. Для многих исключенных исключений для рабочего потока означает, что приложение будет аварийно завершено.
Почему это расположено в первую очередь? – RvdK
@PoweRoy: Я сигнализирую о том, что потоки выходят в методе Dispose элемента управления. Я знаю, что это не лучшая вещь, но я не мог найти лучшего места, чтобы сигнализировать о выходе потоков. –