2014-01-07 19 views
1

我正在理解如何正確使用BeginInvoke。我在一個控制檯應用程序中寫了一個小測試,我所要做的就是使用BeginInvoke調用一個函數來創建一個帶有標題的100x100窗口。我失敗了。這裏是我所擁有的,我知道這可能只是對線程的不理解(不是我強壯的套裝),但是我被困住了,沒有窗口彈出來,我只是在Main的readline處等待按鍵。執行從ThreadUITest開始。Dispatcher.Begin調用正確的用法?

static void ThreadUITest() 
{ 
    ThreadStart starter = new ThreadStart(threadFunc1); 
    Thread test = new Thread(starter); 
    test.IsBackground = true; 
    test.SetApartmentState(ApartmentState.STA); 
    test.Start(); 
} 

static void threadFunc1() 
{ 
    dispatcher = Dispatcher.CurrentDispatcher; //Statically declared earlier 
    ThreadStart starter = new ThreadStart(threadFunc2); 
    Thread test = new Thread(starter); 
    test.IsBackground = true; 
    test.Start(); 
} 

static void threadFunc2() 
{ 
    Action method = Draw; 
    Console.WriteLine("I'm here!"); 
    //dispatcher.BeginInvoke((Action)(() => {Draw();}),DispatcherPriority.Render, null); 
    dispatcher.BeginInvoke(method, DispatcherPriority.Send, null); 
} 

static void Draw() 
{ 
    Window win = new Window(); 
    win.Height = 100; 
    win.Width = 100; 
    win.Title = "A Window!"; 
    win.Show(); 
} 

感謝您的任何幫助。

+0

當你使用'dispatcher.Invoke'時它工作嗎? –

+0

不,它沒有。 –

回答

1

嘗試在threadFunc1的末尾撥打Dispatcher.Run()

+0

好的。那樣做了。新的Dispatcher是否使用線程創建?如果是這樣,爲什麼這個愚蠢的東西不會運行? –

+1

@AaronMarcus創建線程不會自動創建消息循環,不會。大多數線程都沒有消息循環。也就是說,你的程序有很多更大的設計問題,這些問題不能通過簡單地添加這一行來解決。 – Servy

+0

我認爲邏輯是構造函數和屬性不應該阻塞。所以'Dispatcher.CurrentDispatcher'在創建調度程序時不會啓動循環。但是,是的,「Invoke」無法啓動它似乎很奇怪。 –

2

您需要添加在你的threadFunc1

// statically declared earlier, although you don't need to keep a reference to it as 
// WPF will keep it in Application.Current 
application = new Application(); 
application.Run(); // thread1 is now our "UI" thread 

底部下面爲什麼這個解決呢?

Dispatcher對象提供了一個接口,用於讓線程爲您做一些工作(通過BeginInvokeInvoke)。
爲了使線程能夠處理任何「做功」消息,它必須運行某種事件循環,並且等待下一條消息處理 - 如果它沒有這樣做,那麼它將無法處理任何東西,只會卡住。

從線程1調用Dispatcher.CurrentDispatcher將在該線程上創建一個新的調度程序,如果沒有一個已經存在的話[1] - 這會給我們提供將消息發佈到線程的接口。
dispatcher.BeginInvoke的作用是在該線程的消息隊列中添加一個條目,但該線程尚未運行任何消息循環。我們可以給它排隊消息,但它不會接收並運行它們 - 這就是爲什麼沒有發生。

所以,我們需要讓該線程開始運行一個消息循環。
Application.Run()方法是WPF框架方法,它完全如此。方法永遠不會返回(直到您調用Application.Shutdown),它會啓動一個消息循環,以開始處理之後的消息。我認爲將其視爲「接管」該線程是有用的。

這種變化,當thread2func調用 dispatcher.BeginInvoke,裏面 Application.Run消息循環代碼變爲「哦,看,消息,我會處理它」

現在 - 它得到BeginInvoke方法,並做什麼它說(在這種情況下, ,執行你的Draw功能),一切都很好


注:按Ark-kun's answer您也可以只調用Dispatcher.Run啓動該線程的消息循環,而無需創建一個Application對象(Application.Run做到這一點內部) 。一般來說,我覺得更好雖則創建一個應用程序的對象,因爲這是更「正常」,你可能會寫以後可能會出現的其他代碼的應用程序對象存在


[1]僅供參考,這是爲什麼呼叫Dispatcher.CurrentDispatcher是危險的,你應該避免它。如果您從現有UI線程中調用Dispatcher.CurrentDispatcher,它會返回給您正確調度程序的引用。
如果您不小心從另一個線程調用它,在邏輯上,您會認爲它會返回對現有調度程序的引用。但沒有 - 相反,它創建了一個第二個調度程序,指向我們的另一個線程 - 但是我們的另一個線程不會運行消息循環,我們將再次陷入停頓。我建議不要打電話Dispatcher.CurrentDispatcher,除了第一次。因爲所有WPF對象(窗口,按鈕等)都具有Dispatcher屬性,您可以使用它來從任何方向獲取正確的調度程序

+0

調度程序是關於處理抽象事件的。它不是''WPF'特定的,位於'WindowsBase.dll'中。 'Application'位於'PresentationFramework.dll'中,關於'Windows','Resources'和導航。根據情況,人們可能更喜歡簡單的'Dispatcher'而不是全面的'Application'。 –

+0

這是一個有點過度複雜的情況,因爲這是一個測試框架只是爲了擺弄BeginInvoke,以確保我正確使用它,所以我不關心完整的應用程序,感謝您的額外信息! –