2016-03-02 6 views
1

У меня есть ApplicationContext с Form. Различные асинхронные потоки связи и синхронизации могут время от времени перезапускать приложение, однако, прежде чем я смогу перезапустить, мне нужно вручную утилизировать MyApplicationContext, которому необходимо бесплатно освободить ресурсы, которые понадобятся , когда начнется новый процесс.Безопасный вызов в форме. Метод Dispose()

Кажется, что только вызов Application.Restart() не избавляет от ресурсов достаточно быстро в этом случае.

В вызове MyApplicationContext.Dispose() последующий вызов base.Dispose(disposing) в конечном итоге вызывает метод Form.Dispose(), и потому, что это может происходить из различных нитей, я видел исключение операции кросс-нить происходит.

/// MyApplicationContext.requestRestart() 
private void requestRestart() 
{ 
    this.Dispose(); // dispose of applicationcontext 
    Application.Restart(); 
} 

приводит к ...

/// MyApplicationContext.Dispose(bool) 
protected override void Dispose(bool disposing) 
{ 
    /// dispose stuff 
    base.Dispose(disposing); 
} 

приводит к ...

/// MainForm.Dispose(bool) 
protected override void Dispose(bool disposing) 
{ 
    /// dispose stuff 
    base.Dispose(disposing); 
} 

который может быть вызван из любого потока.

Безопасно ли для Invoke переопределяемый обработчик на Form пользовательских интерфейсов?

protected override void Dispose(bool disposing) 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new Action(() => Dispose(disposing))); 
    } 
    else 
    { 
     if (disposing && (components != null)) 
     { 
      components.Dispose(); 
     } 
     base.Dispose(disposing); 
    } 
} 

ответ

2

Нет, это не безопасно вызывать Invoke() от Dispose() реализации.

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

Dispose() способ предположительно простой. Он не должен иметь доступ к управляемым объектам, и он должен завершиться быстро. Вызов Invoke() может привести к неопределенным задержкам, в зависимости от того, что еще происходит в программе, и даже в тупике.

Помимо этого важным правилом в методе Dispose() является отсутствие доступа к управляемым объектам. По крайней мере, ваша реализация должна сначала проверить disposing, чтобы убедиться, что вы не пытаетесь вызвать Invoke() при вызове из финализатора. Кроме того, определенно также не рекомендуется удалять объект, который в настоящее время используется для вызова метода Invoke(). Если вы действительно должны использовать перекрестный вызов, правильный способ сделать это - получить SynchronizationContext для текущего объекта и использовать это вместо вызова Control.Invoke().


Но действительно, правильная вещь, чтобы сделать здесь, - это исправить вашу логику перезагрузки, поэтому вся операция удаления происходит в потоке пользовательского интерфейса, которому принадлежат объекты, которые находятся в распоряжении. Ваши объекты не те, которые несут ответственность за перенос выполнения в правильную нить; код, который использует, этот объект должен быть вместо этого. К тому моменту, когда задействуется код класса Form, вы уже должны убедиться, что выполнение кода в потоке пользовательского интерфейса, которому принадлежит этот объект.

+0

Действительно, я был * ушел * вместе с ним.Я думаю, что будет достаточно легко переместить мой метод 'requestRestart' в класс' Form', так что если требуется вызов перекрестного потока, это делается в нижней части стека вызовов. Но в этом случае невозможно гарантировать, что поток, инициирующий запрос на перезагрузку, будет потоком пользовательского интерфейса «Form'», например, когда придет сеть IO. – khargoosh

+0

Вы можете инициировать запрос на перезагрузку в любом месте, если вы убедитесь, что вы переместились в поток пользовательского интерфейса, прежде чем на самом деле _handling_ очистка, которая предшествует запросу на перезапуск. Даже там вам может показаться полезным/желательно использовать 'SynchronizationContext' вместо экземпляра' Form', так как предположительно, что экземпляр 'Form' будет удаляться как часть очистки (т. Е. Вы все еще имеете проблему вызова' Invoke() 'на объект, который будет удален до того, как метод Invoke() будет возвращен). –

+0

Я принял ваш хороший совет и использую 'SynchronizationContext' из основной формы. Спасибо, Питер. Я чувствую себя намного лучше о том, как это происходит сейчас. – khargoosh