2009-08-18 89 views
2

我目前正在嘗試解決初始化問題,該問題源於所有子組件同步初始化的假設。我可以使異步組件顯示爲同步嗎?

UI實例化一個擁有自己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中實現這一點的任何想法?

更新 - 2009年8月21日
我有一段時間來嘗試今天,這裏是我發現的。

WaitOne - 如果給定足夠大的超時時間,則每次只需停止應用程序即可工作。不幸的是,超時時間至少需要5秒(比我等待的時間長)。沒有超時,它仍然掛起。調用set的事件根本不會發生。

睡覺 - 與WaitOne一樣,在足夠長的超時時間內它似乎工作。

線程 - 我不想讓UI繼續下去,直到完成初始化爲止,因爲UI的行爲被初始化結果改變了。但是,將HelperClass對象的初始化拆分爲單獨的線程並調用Thread.Join來暫停主線程。

所以這個問題的解決方案似乎是以正確的方式使用多個線程。

回答

-1

你爲什麼不掛鉤

helper.StartUp();

到helper.ReadyEvent以及?

+1

Wha?這要麼沒有道理,要麼我的大腦今天完全被炸了...... – 2009-08-18 21:36:11

1

只要調用了helper_ReadyEvent委託,就可以在配置嚮導中添加一個只讀屬性,該屬性設置爲true。然後,您可以輪詢該屬性並在表單準備就緒後顯示對話框。

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

或者,你是否可以在初始化ConfigWizard之前初始化助手類?那麼你可以只提供一個對已經通過類構造函數初始化爲配置表單的helper類的引用?考慮到這裏的回覆數量,在我看來,有很多方法可以完成這項任務。

0

我不明白ManualResetEvent如何在您的情況下失敗。如果在創建HelperClass實例後創建一個實例,並在ReadyEvent中創建Set,則只需在ConfWizard構造函數的底部添加一個WaitOne即可。是的,WaitOne會阻止,但這是行爲(你的ConfWizard構造函數不會返回,直到一切準備就緒),你想要的不是嗎?

+0

阻塞行爲就是我想要的。不幸的是,如果在主線程中調用WaitOne,它也會阻塞消息泵,從而防止處理ReadyEvent。這意味着Set永遠不會調用,然後應用程序被掛起。 – Corin 2009-08-18 22:17:57

+0

是否必須在UI線程中初始化ConfWizard?您是否可以不將初始化委託給後臺線程,以便您的UI線程可以繼續執行與UI相關的任務? – paracycle 2009-08-19 13:45:13

0

我的第一個想法是「使用等待手柄」,但正如您在帖子末尾所說的那樣,該事件不會在UI線程上嘗試提升,但會在等待中等待在UI線程上。

(我想這就是爲什麼它不工作,如果它提出了在後臺線程 - 。只使用一個ManualResetEvent的信號UI線程它已經準備好)

一個替代的解決方案是讓形式顯示,即使在輔助類是沒有準備好,並在輔助類的所有操作重定向到一個動作隊列得到處理時,它已準備就緒:

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需要UI線程。這有點鹹菜 - 因爲你不能真正延遲構造函數返回。

如果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(); 
     }); 
    } 
} 

否則 - 我認爲你要麼必須通過工廠異步創作,或只是與事實打交道是HelperClass可能還沒有準備好並重新處理或禁用UI適當。

編輯:

是[的DoEvents]一樣可怕,因爲它在VB6?

對於manypeople,yes。但是,國際海事組織(它像往常一樣)取決於情況。你必須小心,因爲可能的重入 - 如果你正在運行的窗口消息的結果,那麼你可能需要防範。

FWIW,DoEvents最常見的用途是重畫屏幕,因爲長時間運行操作會導致UI掛起。在那種情況下 - 是的,.NET爲您提供了更好的線程處理方式。但是,如果你只是想要產生UI線程(你這樣做),我(and others)看到一些精心設置的DoEvent調用沒有問題。坦率地說,我認爲這是你的選擇中最簡單的(當然,重寫HelperClass的時間不長)。

+0

我想到了DoEvents之類的東西,但我覺得這是一段VB6,可能並沒有將它變成.NET,我甚至沒有看到它的幫助。它和VB6一樣嚇人嗎? – Corin 2009-08-19 00:34:20