我刚刚开始处理STA / MTA问题,所以对问题的简单性表示道歉。我无法在梯子的底部找到一个我真正理解的答案。
我正在为另一个软件编写一个插件,并且在工作线程中找到了我需要创建一些UI元素的点。我知道我不能从工作线程内部做到这一点,因为它不是一个STA线程,我需要回到Main(或者只是另一个?)STA线程来创建UI元素。一些澄清将有很大帮助。
所有STA线程都具有相同的“权限”,即如果主线程是STA并创建一个Window,则向其添加一些UI元素。然后产生另一个STA线程,第二个线程同样创建一些UI元素,他们是在相同的“空间”中做的(选择不好的词,但我不知道还有什么用),并且可以访问彼此的UI元素没有造成死亡和破坏?或者我是否需要显式跳回主/原始STA线程,并且只从THAT(不仅仅是任何)STA线程创建UI元素?
如果是这种情况(只允许1个STA线程制作UI元素),我该如何正确地做到这一点?我看过许多与此相关的帖子但由于某些原因我无法理解正在发生的事情,并且会喜欢一个真正简单的答案。
请不要'这是一个很酷的方式...'我只需要在执行点的简单方法,我需要一些UI元素跳回到主STA线程,如果这是必要的。
如果没有必要,那么我将使该工作线程成为一个STA线程并继续我的方式,这是公平的吗?或者我在追求灾难?
答案 0 :(得分:3)
如果主线程是STA并创建一个Window,则向其添加一些UI元素。然后产生另一个STA线程,第二个线程同样创建一些UI元素,他们是在相同的“空间” [snip ...] 中进行的,并且可以访问彼此的UI元素而不会导致死亡和破坏?
如果线程A和B都是STA,那么他们每个人都可以创建和更新他们的自己的 UI元素,但不是每个人。任何其他想要影响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
});
等等
答案 1 :(得分:2)
Control.Invoke
。在WPF中,您可以让调度员执行此操作:Dispatcher.Invoke
。的WinForms:
form1.Invoke(/* a delegate for your operation */)
WPF:
window1.Dispatcher.Invoke(/* a delegate for your operation */)
您所做的不是更改“单个公寓”中的对象,而是要求(调用)控制它的STA线程为您执行此操作(您调用的委托)。您还可以BeginInvoke
异步执行此操作。