2009-08-18 4 views
2

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

UI создает экземпляр класса, имеющего собственный интерфейс. Это выглядит примерно так:

ConfWizard cf = new ConfWizard(); 
cf.ShowDialog(); 

Беда в том, что класс ConfWizard использует другой класс, который инициализирует асинхронно, но должен быть готов до ShowDialog вызывается для правильного функционирования. Код ConfWizard выглядит примерно так:

public ConfWizard() 
{ 
    helper = new HelperClass 
    helper.ReadyEvent += new HelperClass.ReadyEventHandler(this.helper_ReadyEvent) 
    helper.StartUp(); 
    // Do more initialization using properties of hc 
} 

private helper_ReadyEvent() 
{ 
    //HelperClass is ready to use 
} 

Поскольку свойства помощника не может быть установлен только перед повышением ReadyEvent, текущий конструктор вообще не инициализируется правильно. Может показаться очевидным положить оставшуюся инициализацию в helper_ReadyEvent, но это приведет к возврату конструктора до того, как объект будет готов к использованию. Поскольку классы, использующие объект ConfWizard, предполагают, что после того, как конструктор вернет объект, он полностью готов к использованию, преждевременное возвращение нежелательно.

К сожалению, я не могу изменить HelperClass, поэтому мне нужно скрыть его асинхронное поведение, чтобы класс ConfWizard можно было использовать синхронно.

Я попытался использовать объект ManualResetEvent (вызов Set в обработчике событий), но вызовы WaitOne блокируются и, таким образом, событие не обрабатывается приложением приложения.

Любые идеи о том, как достичь этого в .NET1.1?

ОБНОВЛЕНИЕ - 21 августа 2009 г.
У меня было время экспериментировать сегодня и вот что я нашел.

WaitOne - если заданный достаточно большой тайм-аут будет работать каждый раз, просто задерживая приложение. К сожалению, этот тайм-аут должен быть не менее 5 секунд (дольше, чем я должен ждать). Без тайм-аута он все еще зависает. Событие, которое вызывает набор, просто никогда не происходит.

Спящий - то же, что и WaitOne, в котором с достаточно длинным таймаутом он будет работать.

Threading - Я не хочу, чтобы пользовательский интерфейс продолжался до тех пор, пока инициализация не будет выполнена, потому что поведение пользовательского интерфейса изменяется по результатам инициализации. Однако разделение инициализации объекта HelperClass на отдельный поток и вызов Thread.Join для приостановки работы основного потока.

Таким образом, решение проблемы, кажется, использует несколько потоков в правильном порядке.

ответ

-1

почему вы не подключить к

helper.StartUp();

к helper.ReadyEvent также?

+1

Какой? Это либо не имеет смысла, либо мой мозг полностью обжарен сегодня .... –

1

Вы взломали его и добавили свойство только для чтения в мастере конфигурации, для которого задано значение true при вызове делегата helper_ReadyEvent. Затем вы можете опросить свойство и показать диалог, как только форма будет готова.

ConfigWizard wiz = new ConfigWizard(); 
while (!wiz.Ready) System.Threading.Thread.Sleep(2000); 
wiz.ShowDialog(); 

Или вы не могли инициализировать класс помощника до инициализации ConfigWizard?Тогда вы можете просто указать ссылку на вспомогательный класс, который был инициализирован в конфигурационную форму через конструктор классов? Учитывая количество ответов здесь, мне кажется, есть много способов, которыми вы могли бы выполнить задачу.

0

Я не понимаю, как ManualResetEvent не работает в вашем случае. Если вы создадите один из них после создания своего экземпляра HelperClass и Set в своем ReadyEvent, вам нужно всего лишь добавить WaitOne в нижней части своего ConfWizard конструктора. Да, WaitOne будет блокировать, но это поведение (что ваш конструктор ConfWizard не возвращается, пока все не будет готово), вы хотите, не так ли?

+0

Поведение блокировки - это то, что я хочу. К сожалению, если вы вызываете WaitOne в основном потоке, он также блокирует насос сообщений, тем самым предотвращая обработку ReadyEvent. Это означает, что Set никогда не звонит, и приложение затем зависает. – Corin

+0

Обязательно ли инициализировать 'ConfWizard' в потоке пользовательского интерфейса? Не можете ли вы делегировать инициализацию в фоновый поток, чтобы ваш поток пользовательского интерфейса мог продолжить выполнение задач, связанных с пользовательским интерфейсом? – paracycle

0

Моя первая мысль была «Использовать дескриптор ожидания», но, как вы сказали в конце своего сообщения, это не сработает, поскольку событие попытается поднять поток пользовательского интерфейса, но его заблокирован, поскольку он ждет в потоке пользовательского интерфейса.

(я предполагаю, что это, почему это не работает Если он поднимает на фоне потока -.. Просто использовать ManualResetEvent сигнализировать интерфейс нитку он готов)

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

private Queue actions = new Queue(); 

public void DoSomethingToHelper() 
{ 
    if(!helperClass.IsReady()) 
    { 
     Action work = new Action(DoSomethingToComponent); 
    } 
    else 
    { 
     // Real work here. 
    } 
} 

Затем, когда он будет готов, вы пройти и процесс все действия:

private helper_ReadyEvent() 
{ 
    foreach (Action action in actions) 
    { 
     action.Invoke(); 
    } 
    actions.Clear(); 
} 
0

Я попытался использовать объект ManualResetEvent (вызов Set в обработчике событий), но вызовы WaitOne блокируются, и, таким образом, событие не обрабатывается приложением приложения.

Это должно означать, что либо ReadyEvent вызывается в том же потоке, что и ctor, либо что HelperClass нуждается в потоке пользовательского интерфейса. Это немного рассол, так как вы не можете задержать конструктор.

Если HelperClass просто нужно обработать какое-либо оконное сообщение, вы можете вставить здесь Application.DoEvents.

class ConfWizard { 
    private ManualResetEvent _init = new ManualResetEvent(false); 

    public ConfWizard() { 
     var h = new HelperClass(); 
     h.ReadyEvent += this.helper_ReadyEvent; 
     h.StartUp(); 

     do { 
      Application.DoEvents(); 
     } while (!_init.WaitOne(1000)); 
    }  

    private void helper_ReadyEvent() { 
     _init.Set(); 
    } 
} 

class HelperClass { 
    public event Action ReadyEvent; 

    public void StartUp() { 
     ThreadPool.QueueUserWorkItem(s => { 
     Thread.Sleep(10000); 
     var e = this.ReadyEvent; 
     if (e != null) e(); 
     }); 
    } 
} 

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

Edit:

Is [DoEvents] так же, как страшно, как это было в VB6?

Для manypeople, yes. Но, ИМО, это (как обычно) зависит от сценария. Вы, do, должны быть осторожны с ним из-за возможного повторного включения - если вы работаете в результате оконного сообщения, тогда вам, возможно, придется защититься от этого.

FWIW, наиболее часто используемым DoEvents является перекрашивание экрана из-за длительного действия, которое иначе зависало бы от пользовательского интерфейса. В этом случае - да, .NET дает вам много лучших способов обращения с потоками. Но если вы просто хотите получить поток пользовательского интерфейса (который вы делаете), я (and others) не вижу проблем с некоторыми экономными и тщательно размещенными вызовами DoEvent.И, честно говоря, я думаю, что это наименее сложный из ваших вариантов (конечно, не переписывая HelperClass).

+0

Я думал о чем-то вроде DoEvents, но я подумал, что это часть VB6, которая, вероятно, не попала в .NET, и я даже не заглянул в нее. Это так же страшно, как в VB6? – Corin

Смежные вопросы