此问题与 this question 的最高评价评论相关:
The STA model is used for COM objects that are not thread safe. That means they do not handle their own synchronization. A common use of this is a UI component. So if another thread needs to interact with the object (such as pushing a button in a form) then the message is marshalled onto the STA thread. The windows forms message pumping system is an example of this.
所以下面的例子让我有点困惑..
Main 用 [STAThread]
注释。
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
我假设,由于 Form1 在 STAThread 中运行,因此从另一个线程发送到 UI 组件的每条消息都会被编码到 STA 线程上。
因此我预计这样的调用会起作用:
public Form1()
{
InitializeComponent();
Thread t = new Thread(() => { this.label1.Text = "test"; });
t.IsBackground = true;
t.Start();
}
正如许多其他帖子中所讨论的,它不会起作用。 Thread t ...
必须替换为 Thread t = new Thread(()=> { this.label1.Invoke((Action)(() => { this.label1.Invoke((Action)(() => { this.label1.Invoke) )) label1.Text = "test"; })); });
来修复它,但请记住,STA 应该编码 this.label1.Text = "test";
到主线程,为什么还是出现无效的跨线程访问错误?
编辑已解决:
接受的答案:
[STAThread]
(and[MTAThread]
) are only related to working with COM interop.
由于 Label-Object 似乎只是 Win32 控件的包装器,因此它不是 COM-Object(由于问题开头的 block 引用,我错误地预期了这一点),因此不会对调用进行编码。包装器内会检查来自其他线程的调用,但必须由开发人员通过 (Begin)Invoke 进行编码。
接下来的问题当然是:如何修改包装器,以便这是自动完成且向下兼容的......但我想这是另一个主题。
最佳答案
STA代表single threaded apartments用于确保一个线程可以同时访问 COM 组件。您得到的异常不是因为 COM 与多个线程同步,而是在未创建 COM 的线程中访问控件。
标签控件是在 GUI 线程中创建的,应该在其创建的线程(即 GUI 线程)上进行访问。您试图在非 GUI 线程上访问它,这就是您收到跨线程异常的原因。 Invoke 方法将访问 GUI 线程上的 Label,并且不会出现跨线程异常。
OP评论
在此示例中 - STA 将从另一个线程对 UI 组件的每个调用编码到 UI 线程上,因此它实际上是在 UI 线程上执行的,因此不再是跨线程。还是编码以不同的方式工作?
编码是一种机制,通过该机制可以使一个公寓可以访问的对象可以被另一个公寓访问,reference 。在 COM 组件上,当您调用方法时,将执行发起单元的调用,并将结果返回给调用的发起者。另一方面,对于非 COM 组件,STA 单元不会起作用。 .net 对非 COM 组件(如标签、按钮等)在创建的线程上访问 GUI 对象的限制必须通过使用 Invoke 等方法来处理,例如当被 GUI 线程以外的线程访问时。
Marshaling 由于公寓模型对象的规则是只能从创建它们的线程访问它们,因此如果您想从另一个线程访问它们,您需要做一些额外的工作:您需要 hire a lackey 。 COM 称这个走狗为“代理”。当您调用代理对象上的方法时,该调用将被路由回原始单元,该方法在其原始单元上执行,然后结果将被路由回原始调用者。 (如果该方法的任何参数本身就是对象,那么 COM 也需要为这些对象创建代理!)编码(marshal)处理是一种创建代理的机制。
COM threading models only apply to applications that use COM interop. The COM threading model can be set to single-threaded apartment or multithreaded apartment. The application thread is only initialized for COM interop if the thread actually makes a call to a COM component. If COM interop is not used, then the thread is not initialized, and the STAThreadAttribute attribute, if it is present, has no effect.
How to: Make Thread-Safe Calls to Windows Forms Controls
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way. It is unsafe to call a control from a thread other than the one that created the control without using the Invoke method. The following is an example of a call that is not thread safe.
关于c# - 尽管 UI 存在于 STAThread 中,为什么 Control.(Begin)Invoke 是必要的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42248954/