.Net中的Single Threading Apartment(STA)模型与Thread-safed线程安全

本文探讨了STA模型的基本概念及其在Win32系统中如何确保UI操作的线程安全。详细介绍了两种方法:Invoke机制和BackgroundWorker类的使用,以实现跨线程的UI更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   最近看了相关的一些这方面的内容,做一个简单的笔记,主要为了备忘,也许能帮上有缘的人。

   STA模型是一种线程模型,这缘于Win32窗口的需要,在Win32系统中的窗口都有一个句柄,这个窗口句柄与创建它的线程的句柄是被联系在一起的(这也是为什么消息泵中的DispathMessage函数能正确投递消息到对应窗口的WndProc的原因)。

    为了简单的说明问题,假设线程A创建了窗口W,同时为了工作需要线程A有创建了工作线程B。

    STA中,对窗口W中与UI(界面)相关属性的修改,必须在创建窗口的线程A中完成。换句话说,线程B不允许修改W的UI属性(否则抛出InvalidOperationException,消息“Control control name accessed from a thread other than the thread it was created on”)。从理论上讲,这种限制是合理的,因为只有从线程A的句柄才能找到窗口W的句柄,从而修改其外观。笔者我也不清楚在Windows Vista中的Avalon会不会改掉窗口线程的模式,毕竟STA模式不适十分方便,且容易引起线程安全问题。

    如果能确保线程B不执行修改W的UI属性的代码,这称为Thread-safed线程安全。.Net中几乎所有的UI组件都是线程安全的,虽然当中有些组件含有多个线程。
   
    当我们创建自定义控件时,也要注意Thread-safed问题。但有时,线程B的确有修改窗口W的UI属性的需要,如何是好?基本上,有两个解决办法。

    方法一:在线程B中调用Control::Invoke(Delegate)或Control::BeginInvoke(Delegate)函数(例如:W->Invoke(d))前者是同步调用,后者异步调用,其中的委托执行修改窗口W的UI属性的代码。这俩个函数的功能是,将Delegate强制放到线程A中执行。

    方法二:线程B不用Thread类创建,而用BackgroundWorker类创建。BackgroundWorker类通过DoWork、ProgressChanged、RunWorkerCompleted三个事件(event)驱动的。DoWork最为重要,它的EventHandler将被执行与新的线程(本例中的线程B),通过调用BackgroundWorker::RunWorkerAsync()触发DoWork。后两个事件的EventHandler将被执行于原线程(本例中的线程A),ProgressChanged由BackgroundWorker::ReportProgress (Int32) 触发,RunWorkerCompleted在DoWork干完事被触发。如此一来,我们可以将修改窗口UI属性的代码放入事件ProgressChanged的EventHandler中。当线程B(DoWork)希望修改窗口UI属性时,用ReportProgress (Int32)触发事件ProgressChanged,由后者的EventHandler完成修改,而它正是执行于线程A。
  
    个人推荐方法二,但是为什么BackgroundWorker的ProgressChanged可以被保留在原线程执行?这是笔者还不懂的问题,要知道,一般情况下,触发一个event,就是在本线程执行EventHandler,难道也使用了方法一中的Invoke?

    不清楚,总觉得一个线程让另一个线程停下来执行一段代码,很是奇妙。希望清楚的兄弟谈谈自己的看法,相互交流一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值