C# 来自MTA的STA呼叫
我刚刚开始处理STA/MTA问题,因此,对于问题的简单性表示歉意。在这个阶梯的底部,我找不到一个我能真正理解的答案 我正在为另一个软件编写插件,在一个工作线程中,我需要创建一些UI元素。我明白我不能从工作线程内部完成这项工作,因为它不是STA线程,我需要返回主(或者只是另一个?)STA线程来创建UI元素。一些澄清将大有帮助C# 来自MTA的STA呼叫,c#,.net,multithreading,sta,mta,C#,.net,Multithreading,Sta,Mta,我刚刚开始处理STA/MTA问题,因此,对于问题的简单性表示歉意。在这个阶梯的底部,我找不到一个我能真正理解的答案 我正在为另一个软件编写插件,在一个工作线程中,我需要创建一些UI元素。我明白我不能从工作线程内部完成这项工作,因为它不是STA线程,我需要返回主(或者只是另一个?)STA线程来创建UI元素。一些澄清将大有帮助 是否所有STA线程都具有相同的“权限”,即如果主线程是STA并创建了一个窗口,则会向其中添加一些UI元素。然后衍生出另一个STA线程,第二个线程同样创建了一些UI元素,它们是
control.invoke
。在WPF中,您可以由调度程序执行:dispatcher.invoke
form1.Invoke(/* a delegate for your operation */)
WPF:
您所做的不是更改“单个单元”中的对象,而是请求(调用)控制它的STA线程(调用的委托)为您执行该操作。您还可以使用BeginInvoke
进行异步操作
如果主线程是STA并创建了一个窗口,则向其中添加一些UI元素。然后产生另一个STA线程,第二个线程同样创建一些UI元素,它们是否在同一个“空间”[snip…]中进行操作,并且可以访问彼此的UI元素而不会造成死亡和破坏
如果线程A和线程B都是STA,那么它们可以各自创建和更新自己的UI元素,但不能相互创建和更新。任何其他想要影响UI的线程都必须使用BeginInvoke
风格的方法之一来请求相应的线程进行更新
如果没有必要,那么我将只让该工作线程成为STA线程并继续我的方式,这公平吗?还是我在制造灾难
如果工作线程已设置为MTA并已初始化,则可能无法使其成为STA线程。你可能得做一条新线
你是怎么做到的?您似乎希望在UI中使用WPF(System.Windows.*
),因此,如果您正在“插入”的应用程序也在使用WPF,您应该能够访问它并重新使用它的UI线程。如果没有,您可以创建一个新线程,并在其上创建一个新的应用程序
,然后调用Run
。这将为您设置一个调度程序
类似这样的东西(伪代码是从其他地方的一些工作代码复制过来的)
等等STA和MTA是与COM编程相关的术语。你真的在做COM吗?如果不是这样,那么只需要关注保持UI线程安全的需要,并使用类库提供的基本原语封送调用。Winforms中的Control.Begin/Invoke(),WPF或应用商店中的Dispatcher.Begin/Invoke()。这是我收到的错误:System.InvalidOperationException:调用线程必须是STA,因为许多UI组件都需要它。因此,我认为这是STA/MTA问题。我并没有明确地使用COM(我知道),但我正在为一个更大的软件编写一个插件,我知道它正在使用COM。你应该更多地了解你正在编写插件的“更大”软件的体系结构。有些应用程序有多线程UI,每个主UI窗口都位于自己的线程中。举几个例子:微软Word,Windows资源管理器。你的主机应用程序会是这种情况吗?我想我正在使用WinForms(C#的新功能),所以想法应该是,首先为我想要创建所有UI元素的线程创建一个Dispatcher对象单例样式,并在需要时随时抓住它?对不起,我想我正在做WPF查看您的编辑。我只是以编程方式创建一个窗口,然后向面板添加控件,并使面板成为该面板的窗口内容。WPF,对吗?调度员是WPF。在winforms中,表单基本上是windows。它们具有作为控件的按钮和文本框。如果您有一些非STA线程想要更新UI。它需要一个对该表单的引用(比如说,_mainForm是名称),只需调用invoke
_mainForm.invoke(()=>_button.Text=“New button”)代码>我没有运行任何表单。软件的主要部分对我来说是一个黑匣子。在我这方面,我正在创建一个新窗口(System.Windows.window win=new window())并向其中添加控件。在这里不想太复杂:)所以我需要在进入辅助线程之前在主线程上提前创建该窗口?@AaronMarcus System。Windows是WPF名称空间,所以您不使用winforms。在这种情况下,您需要使用dispatcher:win.dispatcher.Invoke(()=>/*需要在STA线程下运行的任何代码*/)
Ok。但我仍然需要它
window1.Dispatcher.Invoke(/* a delegate for your operation */)
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
});