c# - 尽管 UI 存在于 STAThread 中,为什么 Control.(Begin)Invoke 是必要的?

标签 c# .net multithreading user-interface

此问题与 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)处理是一种创建代理的机制。

STAThreadAttribute

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/

相关文章:

c# - [DebuggerDisplay(...)] 属性有用的示例?

c# - 我如何编写 linq 来比较年月 f

c# - 从 Controller 动态更改 css

c# - dotnet-razor-tooling.dll 的 .NET Core 1.0 部署问题

c# - 向页面传递参数

java - RxJava 在连接映射后更改线程

c# - 如何使持久的后台任务在 asp.net core 中发出范围内的服务信号?

c# - 我如何设计一个类来接收具有未知数量参数的委托(delegate)?

具有共享只读内存的 C++ 线程池

c - 用于性能测量的多线程合并排序