C# 为什么控件(Begin)调用是必需的,尽管UI存在于线程中?

C# 为什么控件(Begin)调用是必需的,尽管UI存在于线程中?,c#,.net,multithreading,user-interface,C#,.net,Multithreading,User Interface,该问题与以下最热门评论相关: STA模型用于非线程安全的COM对象。这意味着它们不处理自己的同步。它的一个常见用途是UI组件。因此,如果另一个线程需要与该对象交互(例如按下表单中的按钮),则消息将被编组到STA线程上。windows窗体消息泵送系统就是一个例子 所以下面的例子让我有点困惑 Main用[STAThread]注释 [STAThread] static void Main() { Application.EnableVisualStyles(); Application

该问题与以下最热门评论相关:

STA模型用于非线程安全的COM对象。这意味着它们不处理自己的同步。它的一个常见用途是UI组件。因此,如果另一个线程需要与该对象交互(例如按下表单中的按钮),则消息将被编组到STA线程上。windows窗体消息泵送系统就是一个例子

所以下面的例子让我有点困惑

Main用
[STAThread]
注释

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}
我假设,因为Form1在一个STA线程中运行,所以从另一个线程发送到UI组件的每个消息都被封送到STA线程

因此,我希望这样的电话能起作用:

public Form1()
{
    InitializeComponent();

    Thread t = new Thread(() => { this.label1.Text = "test"; });
    t.IsBackground = true;
    t.Start();
}
正如许多其他帖子中所讨论的,它不会起作用<代码>线程t…必须替换为类似以下内容的内容:
线程t=新线程(()=>{this.label1.Invoke((操作)(()=>{this.label1.Text=“test”;);})
来修复它,但要记住,STA应该marshall
this.label1.Text=“test”到主线程,为什么我仍然得到无效的跨线程访问错误

编辑已解决:

接受的答复:

[STAThread]
(和
[MTAThread]
)仅与使用COM相关 互操作

由于Label对象似乎只是Win32控件的包装器,因此它不是COM对象(由于问题开头的blockquote,我错误地期望它),因此不会封送调用。在包装器中会检查来自其他线程的调用,但是通过(Begin)Invoke进行编组必须由开发人员完成

接下来的问题当然是:如何修改包装器,所以这是自动完成的,并且向下兼容。。但我想这是另一个主题。

STA代表用来确保一个线程一次可以访问COM组件。您得到的异常不是因为COM与多个线程的同步,而是在未创建COM的线程中访问该控件

label控件是在GUI线程中创建的,应该在其创建的线程(即GUI线程)上访问它。您正试图在非GUI线程上访问它,这就是为什么会出现跨线程异常。Invoke方法将访问GUI线程上的标签,您不会得到跨线程异常

OP评论

在本例中,STA将从另一个线程到UI线程的每个UI组件调用编组,因此它实际上在UI线程上执行,因此不再是跨线程。还是编组的工作方式不同

封送处理是一种机制,通过该机制,一个单元可以访问的对象可以被另一个单元访问。在COM组件上,当您调用方法时,将执行调用,并将结果返回给调用的发起人。另一方面,对于非COM组件,STA单元将不起作用。net对非COM组件(如Label、Button等)施加的在创建GUI对象的线程上访问GUI对象的限制,必须使用调用等方法来处理,例如,当由非GUI线程的线程访问GUI对象时

由于单元模型对象的规则是只能从创建它们的线程访问它们,因此如果要从另一个线程访问它们,则需要做一些额外的工作:需要。COM称这个仆人为“代理”。在代理对象上调用方法时,调用将路由回原始单元,该方法在其原始单元上执行,然后将结果路由回原始调用方。(如果方法的任何参数本身就是对象,那么COM也需要为这些对象创建代理!)封送是一种创建代理的机制

COM线程模型仅适用于使用COM互操作的应用程序。 COM线程模型可以设置为单线程单元或 多线程单元。应用程序线程仅被初始化 对于COM互操作,如果线程实际调用COM 组成部分。如果未使用COM互操作,则该线程不可用 已初始化,以及STAThreadAttribute属性(如果存在), 没有效果

访问Windows窗体控件本身不是线程安全的。如果你 如果有两个或多个线程操纵控件的状态,则 可能会强制控件进入不一致的状态。其他 可能存在与线程相关的错误,例如争用条件和 僵局。确保对控件的访问权限是很重要的 以线程安全的方式执行。从中调用控件是不安全的 创建控件而不使用 调用方法。下面是一个不可用的调用示例 线程安全


默认情况下.net对象没有任何类型的多线程封送处理

[STAThread]
(和
[MTAThread]
)仅与使用COM互操作相关。通常,这些属性指示如何通过调用初始化线程。因此,真正的编组只有在访问COM对象时才会发生,并且它将由COM子系统而不是.net来完成

UI是另一种野兽。实际上,在后台使用的大多数Win32控件都是线程安全的(因为它们是基于消息范例的),但它们周围的.net包装器却不是。因此,为了防止在使用UI时出现不一致的状态,所有WinForms类都有从不同线程访问它们的特殊检查

总而言之,当从不同的线程访问WinForms类时,不会发生默认的封送处理,只有简单的线程检查,这就是为什么您应该自己进行封送处理。