2014-01-06 98 views
3

我剛剛開始處理STA/MTA問題,因此對於問題的簡單性表示歉意。我在這裏的階梯底部找不到我實際可以理解的答案。來自MTA的STA呼叫

我正在爲另一個軟件寫一個插件,並且需要創建一些UI元素的工作線程中出現一個點。我明白,我不能從工作線程內部執行此操作,因爲它不是STA線程,而且我需要返回Main(或另一個?)STA線程來創建UI元素。一些澄清會有很大幫助。

  1. 是否所有STA線程都具有相同的「權限」,即如果主線程是STA並創建窗口,則向其添加一些UI元素。然後產生另一個STA線程,第二個線程同樣創建一些UI元素,他們是在同一個「空間」(糟糕的詞選擇,但我不知道還有什麼其他用途)並且可以訪問彼此的UI元素沒有造成死亡和破壞?或者,我是否需要顯式跳回到主/原始STA線程,並且只從THAT(而不僅僅是ANY)STA線程創建UI元素?

  2. 如果是這種情況(只有1個STA線程被允許製作UI元素),我該如何做到這一點?我見過很多與此相關的帖子,但由於某些原因,我無法完全理解發生了什麼,並且會喜歡真正簡單的答案。

請沒有「這是做......的涼爽光滑的方式」我只需要在執行點簡單的方法,我需要一些UI元素跳回到到主STA線程,如果那是什麼必要。

如果沒有必要,那麼我只會讓該工作線程成爲STA線程並繼續行進,這是否公平?或者我在追求災難?

+1

STA和MTA是相關的COM編程術語。你真的在做COM嗎?如果不是,那麼只關注保持UI線程安全的需要,並使用您用來編組調用的類庫提供的基本基元。 Winforms中的Control.Begin/Invoke(),WPF或Store應用程序中的Dispatcher.Begin/Invoke()。 –

+0

這是我得到的錯誤: System.InvalidOperationException:調用線程必須是STA,因爲許多UI組件需要此。 因爲這個,我認爲它是一個STA/MTA問題。我沒有明確地使用任何COM(我知道),但是我正在爲一個(更多)更大的軟件寫一個插件,我知道它正在使用COM。 –

+0

您應該更多地瞭解您正在編寫插件的那個「更大」的軟件的體系結構。一些應用程序具有多線程UI,其中每個主UI窗口位於其自己的線程中。僅舉幾例:MS Word,Windows資源管理器。您的主機應用程序可以這樣嗎? – Noseratio

回答

2
  1. 如果一個線程創建一個控件。只有這個特定的線程可以與它進行交互,即使有其他STA線程也是如此。
  2. 在WinForms中,您將調用控件上的方法:Control.Invoke。在WPF中,您有調度程序執行此操作:Dispatcher.Invoke

的WinForms:

form1.Invoke(/* a delegate for your operation */) 

WPF:

window1.Dispatcher.Invoke(/* a delegate for your operation */) 

你做什麼,而不是在 「單身公寓」 改變一個對象,你問(調用)的STA線程的控制它爲你做(爲你調用的代表)。你也有異步做BeginInvoke

+0

我想我正在做的WinForms(新的C#),所以這個想法將是什麼,一開始創建一個Dispatcher對象單線程風格的線程我想創建所有的UI元素,並只需要抓住他,只要我需要他? 對不起,我想我正在做WPF看你的編輯。我只是以編程方式創建一個窗口,然後將控件添加到面板,並將面板作爲該窗口的內容。 WPF,對嗎? –

+0

調度員是wpf。在winforms中你有基本上是窗口的窗體。他們有控制按鈕和文本框。如果您有一些非STA線程想要更​​新UI。它需要對該表單的引用(假設_mainForm是名稱),並調用'_mainForm.Invoke((=)_button.Text =「New Button」);' – i3arnon

+0

我沒有任何表單運行。主要的軟件對我來說是一個黑盒子。在我身邊,我正在創建一個新窗口(System.Windows.Window win = new Window())並向它添加控件。不要在這裏過於棘手:)所以我需要在主線程上提前創建該窗口,然後才能切換到輔助線程? –

2

如果主線程是STA並創建一個Window,則向它添加一些UI元素。然後產生另一個STA線程,第二個線程同樣創建一些UI元素,他們是否在同一個'空間'[snip ...]中執行它,並且可以訪問對方的UI元素而不會導致死亡和破壞?

如果線程A和B都是STA,那麼他們可以各自創建和更新其自己 UI元素,但不是eachothers。任何其他想要影響UI的線程都必須使用BeginInvoke樣式方法之一來請求相應的線程進行更新。

如果沒有必要,那麼我只會讓該工作線程成爲STA線程並繼續行進,這是否公平?或者我在追求災難?

如果已將工作線程設置爲MTA並初始化,您可能無法將工作線程作爲STA線程。你可能不得不做一個新的線程。

你怎麼做到的?看起來你想爲你的UI使用WPF(System.Windows.*) - 所以如果你「插入」的應用程序也使用WPF,你應該可以訪問它並重用它的UI線程。如果沒有,您可以創建一個新線程,並在其上創建一個新的Application並致電Run。這應該爲你設置一個調度員。

是這樣的(僞代碼排序的一些工作的代碼複製我在其他地方)

Dispatcher dispatcher = null; // we 'get to' the UI thread via the dispatcher 

if(Application.Current) { // re use an existing application's UI thread 
    dispatcher = Application.Current.Dispatcher; 
} else { 
    var threadReadyEvent = new ManualResetEvent(false); 

    var uiThread = new Thread(() => { 
     Thread.CurrentThread.SetApartmentState(ApartmentState.STA); 

     var application = new Application(); 
     application.Startup += (sender, args) => { 
      dispatcher = application.Dispatcher; 
      threadReadyEvent.Set(); 
     }; 

     // apps have to have a "main window" - but we don't want one, so make a stub 
     var stubWindow = new Window { 
      Width = 1, Height = 1, 
      ShowInTaskbar = false, AllowsTransparency = true, 
      Background = Brushes.Transparent, WindowStyle = WindowStyle.None 
     }; 
     application.Run(stubWindow); 
    }){ IsBackground = true }; 

    uiThread.Start(); 
    threadReadyEvent.WaitOne(); 
    threadReadyEvent.Dispose(); 
} 

dispatcher.Invoke(() => { 
    // ask the UI thread to do something and block until it finishes 
}); 

dispatcher.BeginInvoke(() => { 
    // ask the UI thread to do something asynchronously 
}); 

等等