[转]COM线程模型-套间

COM线程模型-套间

来源: http://blog.csdn.net/crybird/archive/2008/10/11/3057067.aspx
查找了好多资料,终于对套件这一概念有一点心得,赶紧记录下来。

首先,只要遵守COM规范,不用COM库也能编写COM程序,那相当于自己实现用到的COM库函数。本篇COM如果单独出现,指COM库。

1 进程、线程回顾

《WINDOWS核心编程》对进程和线程有深入解释,一个程序运行起来,需要一个进程作为容器。进程管理所有系统资源、内存、线程等等。线程是CPU的调度单位,有自己的栈和寄存器状态。程序最初创建的线程叫主线程,主线程可以创建子线程,子线程还可以创建子线程。

不同进程之间是无法直接通信的,因为它们在虚拟内存中的地址不一样。但操作系统通过LPC机制,可以完成不同进程之间的通信。


COM在进程间通信的方法是本地过程调用(LPC),因为操作系统知道各个进程的确切逻辑地址,所以操作系统可以完成这一点。不同进程间传递的参数需要调整,LPC技术可以完成普通数据的直接拷贝(甚至包括自定义类和指针),但对于接口参数,COM实现了IMarshal接口以调整。

为了可以用同样的方式和进程外、远程组件通信,客户端不直接和组件通信,而是和代理/存根通信,代理/存根是(而且必须是) DLL形式,能完成参数调整和LPC调用。代理存根不用自己写,系统会自动产生。

注:接口的调整,包括列集和散集两种marshal/unmarshal。

2 COM线程模型

2.1 分清模型与实现

看过《Inside C++ Object Model》(中文名:深入C++对象模型;侯捷译)的人都知道,C++对象模型有三种,各家编译器都选择其中效率最高的一种实现出来。另外两种就留在了理论世界,实现出来没有太大意义。提这个的原因,就是为了弄清楚这一点:COM线程模型只是理论构想,是一种抽象的数学模型,还要COM库通过各种手段实现出来,才能为我们使用。

2.2 套间的由来

最开始的COM库,支持的使用组件的唯一模式是single-thread-per-process模式。这样就避免了多线程的同步,而且组件执行的线程肯定是创建它的线程。

然而组件对象真正的执行环境很复杂。COM组件的执行环境有两种:单线程环境Single-Thread,多线程环境Multi-Thread。单线程要考虑执行线程是否是创建组件的线程;多线程还要考虑并发、同步、死锁、竞争等问题。无论哪种环境,都要编写大量的代码以使COM组件对象正确的运行。

为了使程序员减轻痛苦,COM库决心提供一套机制来帮助程序员。如果我们都遵从这套机制,只要付出较少的劳动,就可以让组件对象和COM库一起完成工作。COM库这套机制的核心技术就是“套间技术”。

2.3 COM的多线程模型

2.3.1 COM库的规定

关于多线程问题方面,COM库做出了如下规则(不是COM标准,是COM库为了简化多线程编程中对组件的调用而制定的):

1. COM库提供两种套间,单线程套间和多线程套间,COM组件的编写者最好提供对应的属性(后面会提到),COM组件的使用者要在套间里创建和调用组件。

2. COM库对所有的调用进行参数调整(如果需要),不管是对进程内服务器的调用,还是对进程外服务器的调用。

3. 线程内调用、跨线程调用、跨进程调用都用统一的方式。需要用代理的会用代理。


如此COM规定了COM库、组件编写者、组件使用者三方合作关系。COM库进行协调关系,会根据组件的能力,在不同环境(套间)中创建和调用组件;编写者要说明组件可以生存的环境;调用者查询接口,合理调用。

2.3.2 单线程套间STA

Single-threaded Apartments,一个套间只关联一个线程,COM库保证对象只能由这个线程访问(通过对象的接口指针调用其方法),其他线程不得直接访问这个对象(可以间接访问,但最终还是由这个线程访问)。

COM库实现了所有调用的同步,因为只有关联线程能访问COM对象。如果有N个调用同时并发,N-1个调用处于阻塞状态。对象的状态(也就是对象的成员变量的值)肯定是正确变化的,不会出现线程访问冲突而导致对象状态错误。

注意:这只是要求、希望、协议,实际是否做到是由COM决定的。这个模型很像Windows提供的窗口消息运行机制,因此这个线程模型非常适合于拥有界面的组件,像ActiveX控件、OLE文档服务器等,都应该使用STA的套间。

2.3.3 多线程套间MTA

Multithreaded Apartments,一个套间可以对应多个线程,COM对象可以被多个线程并发访问。所以这个对象的作者必须在自己的代码中实现线程保护、同步工作,保证可以正确改变自己的状态。

这对于作为业务逻辑组件或干后台服务的组件非常适合。因为作为一个分布式的服务器,同一时间可能有几千条服务请求到达,如果排队进行调用,那么将是不能想象的。

注意:这也只是一个要求、希望、协议而已。

2.3.4 COM+新增NA

COM+为了进一步简化多线程编程,引入了中立线程套间概念。

NA/TNA/NTA,Neutral Apartment/Thread Neutral Apartment / Neutral Threaded Apartment。这种套间只和对象相关联,没有关联的线程,因此任何线程都可以直接访问里面的对象,不存在STA的还是MTA的。

2.4 到底什么是套间

根据《COM技术内幕》的观点,COM没有定义自己新的线程模型,而是直接利用了Win32线程,或者说对其做了改造、包装。线程间的同步也是直接用的Win32 APIs。

《COM本质论》设这样定义的:套间定义了一组对象的逻辑组合,这些对象共享一组并发性和冲入限制。每个COM对象都属于某一个套间,一个套间可以包含多个COM对象。

MSDN上解释说,可以把进程中的组件对象想象为分成了很多组,每一组就是一个套间。属于这个套间的线程,可以直接调用组件,不属于这个套间的线程,要通过代理才能调用组件。

最直接的说,COM库为了实现简化多线程编程的构想,提出了套间概念。套间是一个逻辑上的概念,它把Win32里的线程、组件等,按照一定的规则结合在一起,并且以此提供了一种模式,用于多线程并发访问COM组件方面。可以把套间看作COM对象的管理者,它通过调度,切换COM对象的执行环境,保证COM对象的多线程调用正常运行。COM和线程不是包含关系,而是对应和关联关系。

3 第一方COM库:模型的实现

3.1 单线程套间STA

CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);这句代码创建了一个STA,然后套间把当前的线程和自己关联在一起,线程被标记为套间线程,只有这个线程能直接调用COM对象。

在创建套间的时候,COM创建了一个隐藏的窗口。关联线程对组件的调用直接通过接口指针调用方法;其他线程对套间里的对象的调用,都转变成对那个隐藏窗口发送消息,然后由这个隐藏窗口的消息处理函数来实际调用组件对象的方法。编写组件代码的时候,只需调用DispatchMessage即可将方法调用的消息和普通的消息区分开来(通过隐藏窗口的消息处理函数)。

由于窗口消息的处理是异步的,所以所有的调用都是依次进行的,不必考虑同步的问题。只要调用的时候,参数进行合理调整即可(COM库会做到这一点)。但是对于全局变量和静态变量,组件编写者还是要费心的。


一个STA只关联一个线程, single-thread-per-process模式只是STA的一个特例。使用这种模式的线程叫套间线程(Apartment Thread)。

3.2 多线程套间MTA

CoInitializeEx(NULL, COINIT_MULTITHREADED);第一次如此调用的时候,会创建一个MTA,然后套间把当前线程和自己关联在一起,线程被标记为自由线程。以后第二个线程再调用(在同一进程中)的时候,这个MTA会把第二个线程也关联在一起,并标记为自由线程。一个MTA可以关联多个线程。

所有的关联线程都可以调用套间中的组件。这就涉及到同步问题,需要组件编写者解决。

一个MTA可以关联一个或多个线程,这种模式下,COM组件自己要考虑同步问题。使用这种模式的这些线程叫做自由线程(Free Thread) 。

3.3 总结

一个进程可以有0个、1个或多个STA,还可以有0个或1个MTA。


一个线程,进入(或创建)套间后,不能改变套间模式;但可以退出套间,然后以另外的模式再进入(或创建)另一个套间。


在一个进程中,主套间是第一个被初始化的。在单线程的进程里,这是唯一的套间。调用参数在套间之间被调整,COM库通过消息机制处理同步。

如果你设计多个线程作为自由线程,所有的自由线程在同一个单独的套间中,参数被直接(不被列集)传递给这个套间的任何线程,而且你要处理所有的同步。

在既有自由线程又有套间线程的进程里,所有自由线程在一个套间里,而其他套间都是单线程套间。而进程是包含一个多线程套间和N个单线程套间的容器。


COM的线程模型为客户端和服务器提供了这样一种机制:让不同的线程协同工作。不同进程内,不同线程之间的对象调用也是被支持的。以调用者的角度来看,所有对进程外对象的调用都是一致的,而不管它在怎样的线程模型。以被调用者的角度来看,不管调用者的线程模型如何,所获得的调用都是一致的。


客户端和进程外对象之间的交互也很直接,即使它们使用了不同的线程模型,因为它们属于不同的进程。COM介入了客户端和服务器之间,通过标准的列集和RPC,并提供了跨线程操作的代码。

4 第二方COM组件的调用者

4.1 各种调用

4.1.1 同一线程中的调用

同步问题:不需要,调用者和组件在同一线程中,自然同步。

调整问题:不需要,COM库不需要任何介入,参数也不需要调整,组件也不必线程安全。

调用地点:当前线程

这是最简单的情况。

4.1.2 套间线程之间的调用

同步问题:COM库对调用进行同步。

调整问题:不管两个套间是否在同一进程,都需要调整。某些情况下,需要手动调整。

调用地点: 对象所在套间线程。

4.1.3 自由线程之间的调用

同步问题:COM不进行同步,组件自己同步。

调整问题:同一进程不调整,不同进程要调整。

调用地点:客户线程。

4.1.4 自由线程调用套间线程的组件

同步问题:COM库对调用进行同步。

调整问题:不管两个套间是否在同一进程,都需要调整。某些情况下,需要手动调整。

调用地点:套间线程

4.1.5 套间线程调用自由线程的组件

同步问题:COM不进行同步,组件自己同步。

调整问题:需要调整,同一进程,COM会优化调整。

调用地点:客户线程。

4.2 手工调整

如果通过COM方法,所有的参数都由COM库进行调整。有时候需要程序员手工对接口指针进行列集marshal和散集unmarshal,那就是在跨越套间边界,但没有通过COM库进行通信的时候。更明确的说,不通过COM接口函数,通过我们自己写的函数跨套间传递接口指针的时候。


情况一:跨套间传递接口指针。

情况二:类厂在另外的套间中,创建类实例,并传回给客户端的接口指针。


列集函数:CoMarshalInterThreadInterfaceInStream

散集函数:CoGetInterfaceAndReleaseStream

5 第三方COM组件的编写者

组件将在哪种类型的套间中执行,是编写者决定的。对于进程外组件,要调用CoInitializeEx并指定参数,以显示确定套间类型。对于进程内的服务器来说,因为客户端已经调用CoInitializeEx产生套间了,为了允许进程内的服务器可以控制它们的套间类型,COM允许每个组件有自己不同的线程模型,并记录在注册表中。

HKEY_CLASSES_ROOT/CLSID/.../InprocServer32 键值ThreadingModel

5.1 线程模型属性

组件编写者可以实现:同一个组件,既可以在STA中运行,也可以在MTA中运行,还可以在两中环境中同时存在。可以说组件有一种属性说明可以在哪种环境中生存,属性名叫做“线程模型”(相当于“隐藏”)也未尝不可。COM+里真正引入了这个属性,并叫做ThreadModel。这个属性可以有如下取值:

1. Main Thread Apartment

2. Single Thread Apartment (Apartment)

3. Free Thread Apartment (Free)

4. Any Apartment (Both)

5. Neutral Apartment (N/A)

5.2 对象在哪个套间创建

下表中第一列为套间种类,第一行为对象线程模型属性。那么,结果就是在这样的套间中创建这样的组件,组件在什么地方。在必要的时候,会创建一个代理,就是表中的宿主。

未指定

Apartment

Free

Both

Neutral

单线程

(非主)

主STA

当前套间

MTA

当前套间

NA

单线程

(主线程)

当前套间

当前套间

MTA

当前套间

NA

多线程

主STA

宿主STA

MTA

MTA

NA

Neutral

单线程

主线程套间

宿主STA

(本线程)

MTA

NA

NA

Neutral

多线程

主线程套间

宿主STA

MTA

NA

NA




5.3 属性的选择

原则是根据组件的功能选择:

如果组件做I/O,首选Free,因为可以相应其他客户端调用。

如果组件和用户交互,首选Apartment,保持消息依次调用。

COM+首选N/A。

如果没有定义,COM库默认为是Main Thread Apartment。

Apartment简单,Free强大但要自己同步。

6 鸣谢

《COM技术内幕》《COM本质论》《深入解析ATL》

6.1 MSDN2008

在MSDN 2008中相关文档的位置:

Win32和COM开发

-组件开发

-组件对象模型

-SDK文档

-COM

-COM Fundamentals

-Guide-Processes, Threads, and Apartments

Win32和COM开发

-组件开发

-COM+

-SDK文档

-COM+(组件服务)

-COM+开发浏览

-COM+ Contexts and Threading Models

6.2 COM线程模型

http://hi.baidu.com/zhangqiuxi/blog/item/ca7aa52b0311b4fbe6cd401e.html

6.3 理解 COM 套间

http://www.vckbase.com/document/viewdoc/?id=1597

6.4 泛说"COM线程模型"

http://blog.csdn.net/guogangj/archive/2007/09/06/1774280.aspx

6.5 附泛说一文


COM线程模型在COM相关的基础知识中应该算是难点,难的原因可能有这些:


1、需要对COM其它基础知识有较深的了解,因为这个论题几乎涉及到了COM所有其它的基础知识。


2、学习者得非常了解Win32本身的线程模型,因为在Windows中COM的线程模型在建立在Win32线程模型的基础上的。


3、COM线程模型所引用的概念十分抽象,不好理解。


如果你还没有掌握 1,2 所提到的知识点,你可以马上找一些书籍,迅速补充这些知识,如果你已经掌握了这些知识,那就给你的想象力上点油,轻松点。




6.5.1 开始想象


术语


公寓(Apartment)有的译文译作"套间"。这个术语抽象的是COM对象的生存空间,你还真的可以想象成公寓,线程就是住在公寓里的人。


单线程公寓(Single-Threaded Apartment STA) 这种房间是供有钱人住的单人间,设备齐全,服务周到。


多线程公寓(Multithreaded Apartment MTA) 住在这种房间里的人条件就差多了,那么多人就挤在一个大房间里头,可是他们自强不息。个个健壮得不得了。




然后思考


单线程公寓与多线程公寓的本质差别有哪些?


如果另一个人要和住在单线程公寓的人通信,不能直接去找他,哪怕你也住在高贵的单人间。但你可以打电话。提醒一下,电话每次只能同时与一个人说话(他们还没有用到电话会议之类的服务)。住在多线程公寓的人他们的房间有个大窗子,如果住在单人间(单线程公寓)的人想与他们通信,来窗口说就行,而且这个窗子比你想的要大,可以同时让很多人对话。同一房间里的人不用说了,他们可以直通话。




6.5.2 回到现实


术语


1、公寓,如果从来就不用考虑线程同步的问题,就用不着这个概念了,可是 COM 决定支持强大的多线程,于是引入了这个概念,公寓决定了线程与外界通信的方式。每一个与COM对象打交道的线程必须先决定要进入哪种公寓。


2、单线程公寓,这种公寓本身只能包含一个线程,通过调用CoInitialize(NULL)进入。它有着与窗口类似的运作方式,回想一下窗口的运行方式:消息泵不断的从消息队列提取消息,然后调度给相应的窗口函数。这样做的好处是,窗口函数永远不会重入,也就是说永远不会有同步的问题。单线程公寓也用了同样的运作方式(所以该公寓中的线程的主函数必须有一个消息循环):对该公寓中线程所拥有的COM对象的调用被队列化,只有当一个调用结束后,另个调用才会开始。那么组件对象的代码也是永远不会重入。


3、多线程公寓,这种公寓可以包含任意多的线程(具体数目由操作系统决定)。一个进程里头只能包含一个这种公寓,所有调用 CoInitializeEx(NULL, COINIT_MULTITHEADED)都会进入这个公寓。对该公寓中线程所拥有的COM对象的调用是直接的(先不考虑跨进程的情况),包括本公寓中的线程与其它的STA线程。




然后思考


单线程公寓与多线程公寓 的本质差别有哪些?


单线程公寓实现同步,有很多COM库的干预,包括将外部的调用转化成窗口消息,然后那个特别的隐藏窗口的窗口函数把窗口消息转化成COM对象的函数调用。这样的模型可以减小开发组件的难度,可是,却牺牲了效率。多线程公寓把实现同步任务全部交给了组件自己,所以在这种公寓中生存的COM对象必须足够健壮,考虑各种同步问题,不至于多个线程在调用对象的成员函数时会打架。




6.5.3 弄清它们的关系


弄清公寓,线程,对象的关系是很重要的,你弄清了吗?如果你没有弄清,那上面的这些也一定也是看得懵懵懂懂。公寓是这里面最大的单位,它是线程的容器。如果调用CoInitialize(0),COM库会创建一个STA(注意,是"创建"),你的线程将属于这个公寓,并且是这个公寓的唯一成员。如果 CoInitializeEx(NULL, COINIT_MULTITHEADED),而且是第一个要求进入MTA的线程,COM库会创建一个MTA,其它后面调用 CoInitializeEx(NULL, COINIT_MULTITHEADED)的线程会直接进入(注意,我用的"进入")已有MTA。本来线程是一个运行的实体,不会分配资源,可是在COM的线程模型里一个对象与创建它的线程是紧密相关的,称对象归属于某个线程,至于这种归属关系是在COM库内怎么管理,我们先不去管它,以后我们把线程A创建的对象说成是线程A的对象就行了(有一个例外,得说说,有一种Single 类型的COM对象,这种对象基实就是COM在提出线程模型前的产物,这种对象总是归属于主STA线程,即第一个调用CoInitialize(0)的线程。)


在这一部分我将讲解COM提出的各个类型的线程模型,并说明COM运行时期库是如何实现它们的。

本文讲解COM提出的各个类型的线程模型,再说明COM运行时期库是如何实现它们的.

线程模型是一种数学模型,专门针对多线程编程而提供的算法,但也仅是算法,不是实现。本文讲解COM提出的各个类型的线程模型,再说明COM运行时期库是如何实现它们的,就像说明Windows是如何实现线程这个数学模型的一样,最后指明一下跨套间调用和各种类型套间编写的要求以帮助理解。希望读者对于Windows操作系统的线程这个概念相当熟悉,对何谓“线程安全的”亦非常了解。

COM线程模型

COM提供的线程模型共有三种:Single-Threaded Apartment(STA 单线程套间)、Multithreaded Apartment(MTA 多线程套间)和Neutral Apartment/Thread Neutral Apartment/Neutral Threaded Apartment(NA/TNA/NTA 中立线程套间,由COM+提供)。虽然它们的名字都含有套间这个词,这只是COM运行时期库(注意,不是COM规范,以下简称COM)使用套间技术来实现前面的三种线程模型,应注意套间和线程模型不是同一个概念。COM提供的套间共有三种,分别一一对应。而线程模型的存在就是线程规则的不同导致的,而所谓的线程规则就只有两个:代码是线程安全的或不安全的,即代码访问公共数据时会或不会发生访问冲突。由于线程模型只是个模型,概念上的,因此可以违背它,不过就不能获得COM提供的自动同步调用及兼容等好处了。

STA 一个对象只能由一个线程访问(通过对象的接口指针调用其方法),其他线程不得访问这个对象,因此对于这个对象的所有调用都是同步了的,对象的状态(也就是对象的成员变量的值)肯定是正确变化的,不会出现线程访问冲突而导致对象状态错误。其他线程要访问这个对象,必须等待,直到那个唯一的线程空闲时才能调用对象。注意:这只是要求、希望、协议,实际是否做到是由COM决定的。如上所说,这个模型很像Windows提供的窗口消息运行机制,因此这个线程模型非常适合于拥有界面的组件,像ActiveX控件、OLE文档服务器等,都应该使用STA的套间。

MTA 一个对象可以被多个线程访问,即这个对象的代码在自己的方法中实现了线程保护,保证可以正确改变自己的状态。这对于作为业务逻辑组件或干后台服务的组件非常适合。因为作为一个分布式的服务器,同一时间可能有几千条服务请求到达,如果排队进行调用,那么将是不能想像的。注意:这也只是一个要求、希望、协议而已。


NA 一个对象可以被任何线程访问,与MTA不同的是任何线程,而且当跨套间访问时(后面说明),它的调用费用(耗费的CPU时间及资源)要少得多。这准确的说都已经不能算是线程模型了,它是结合套间的具体实现而提出的要求,它和MTA不同的是COM的实现方式而已。
COM套间

  Apartment被翻译成套间或是单元,是线程模型的一个实现者,就像在操作系统课程中讲到的线程只是一个数学模型,而Windows的线程、进程是它(数学模型的线程、进程)的实现者。套间只是逻辑上的一个概念,实现时只是一个结构(由COM管理)而已,记录着相关信息,如它的种类(只能是上面那三个,至少现在是),并由COM根据那个结构进行相应的处理。下面说明这三种套间的实现方式:

STA套间 一个套间如果是STA,那么那个套间有且只有一个线程和其关联,有多个对象或没有对象和其关联,就像有多个线程和一个进程关联一样,也就是说套间那个结构和某个线程及多个对象之间有关系,关系具体是什么由COM说得算,幸运的是COM正是按照上面的线程模型来定义互相之间关系的。根据上面的算法,很容易就知道只有这个线程可以访问这个套间里的对象。

COM是通过在STA套间里的线程中创建一个隐藏窗口,然后外界(这个套间外的线程)对这个对象的调用都转变成对那个隐藏窗口发送消息,然后由这个隐藏窗口的消息处理函数来实际调用组件对象的方法来实现STA的规则的。之所以使用一个隐藏窗口是为了方便组件代码的编写——只需调用DispatchMessage即可将方法调用的消息和普通的消息区分开来(通过隐藏窗口的消息处理函数)。外界对这个对象的调用都将转变成对这个隐藏窗口的消息发送来实现同步。至于COM如何截获外界对对象的调用,则是利于代理对象,后面再说明。

值得注意的是,如果使用标准汇集法生成代理对象,则代理对象会根据是进程内还是进程外的跨套间调用,来决定具体操作。如果外界线程和STA线程在同一进程内,则代理对象将直接向STA线程中的隐藏窗口发送消息;如果不在同一进程内(包括远程进程),代理对象将向RPC管理的一个线程池请求一个线程(RPC线程)来专门向另一进程中的STA线程的隐藏窗口发送消息,而不是代理对象直接发送消息,以防止外界线程由于网络等不稳定因素而导致挂起。

因为COM利用消息机制来实现STA,因此STA套间里的线程必须实现消息循环,否则COM将不能实现STA的要求。

MTA套间 这种类型的套间可以和多个线程及多个或没有对象相关联。根据上面的MTA模型,可知只有这个套间里的线程才能访问这个套间里的对象,和STA不同的只是可以多个线程同时访问对象。

外界(不属于这个套间的线程)对这个套间里的对象的调用将会导致调用线程(外界线程,也就是STA线程,因为NA没有线程)挂起,然后向RPC管理的一个线程池请求一个线程(RPC线程,并已经进入了这个MTA套间)以调用那个对象的方法。对象返回后,调用线程被唤醒,继续运行。虽然可以让STA线程直接调用对象(而不用像前述的挂起等待另一个线程来调用对象),但这是必须的,因为可能会有回调问题,比如这个MTA线程又反过来回调外界线程中的组件对象(假设客户本身也是一个组件对象,这正是连接点技术),如果异步回调将可能发生错误。

反过来,MTA的线程访问STA里的对象时,COM将把调用转换成对STA线程里那个隐藏窗口的一个消息发送,返回后再由COM转成结果返回给MTA的线程(如果使用标准汇集法生成标准代理对象,则发生的具体情况就如上面STA套间所述)。因此STA和MTA都是只能由它们关联的线程调用它们关联的对象。而根据上面所说,当MTA调STA或STA调MTA,都会发生线程切换,也就是说一个线程挂起而换成执行另一个线程。这是相当大的消耗(需要从内核模式向用户模式转换,再倒转好几回),而NA就是针对这个设计的。

NA套间 这种套间只和对象相关联,没有关联的线程,因此任何线程都可以直接访问里面的对象,不存在STA的还是MTA的。

外界(其实就是任何线程)对这个套间里面的调用都不需要挂起等待,而是进入NA套间,直接调用对象的方法。NA套间是由COM+提供的,COM+中的每个对象都有一个环境和其相绑定,环境记录了必要的信息,并监听对对象的每一次调用,以保证当将对象的接口指针成员变量进行传递或回调时其操作的正确性(保证执行线程在正确的套间内,MTA线程就是通过将自己挂起以等待STA线程的消息处理完毕来保证的),从而避免了调用线程的挂起,因此这个代理(其实也就是环境的一部分)被称作轻量级代理(相对于STA套间和MTA套间的重量级代理——需要挂起调用线程,发生线程切换)。

这个轻量级代理并不是永远都不发生线程切换。当NA对象里有个对指向一个STA对象的指针的调用而调用线程不是那个STA对象关联的线程时,调用将会转成向被调用的STA对象的关联线程发送消息,此时照样会发生线程切换。同理,如果那个对象是MTA的,而调用线程是STA线程时,依旧发生线程切换。不过除此以外的大多数情况(即不在NA对象的方法中调用另一个套间对象的方法)都不会发生线程切换,即使出现上面的情况也只有必要(MTA调NA再调MTA就不用切换)才切换线程。

根据上面所说,STA其实和MTA逻辑上是完全一样的,只是一个是关联一个线程,一个是关联多个线程而已。但把它们分开是必要的,因为线程安全就是针对是一个线程还是多个线程。而NA之所以不关联线程是因为它的目的是消除上面跨套间调用时产生的线程切换损耗,关联线程没有任何意义。

COM强行规定(不遵守也没辙,因为全是COM实现套间的,根本没有插手的余地)一个进程可以拥有多个STA的套间,但只能拥有一个MTA套间和一个NA套间,我想这应该已经很容易理解了(要两个MTA套间或NA套间干甚?)。
套间生成规则

  线程在进行大多数COM操作之前,需要先调用CoInitialize或CoInitializeEx。调用CoInitialize告诉COM生成一个STA套间,并将当前的调用线程和这个套间相关联。而调用CoInitializeEx( NULL, COINIT_MULTITHREADED );告诉COM检查是否已经有了一个MTA套间,没有则生成一个MTA套间,然后将那个套间和调用线程相关联。接着在调用CoCreateInstance或CoGetClassObject等创建对象的函数时,创建的对象将以一个特定规则决定和哪个套间相关联(后叙)。这样完成后,就完成了线程、对象和套间的关联(或绑定)。

前面提到的决定对象去向的规则如下。

当是进程内组件时,根据注册表项<CLSID>\InprocServer32\ThreadingModel和线程的不同,列于下表:

创建线程关联的套间种类 ThreadingModel键值 组件对象最后所在套间
STA Apartment 创建线程的套间
STA Free 进程内的MTA套间
STA Both 创建线程的套间
STA ""或Single 进程内的主STA套间
STA Neutral 进程内的NA套间
MTA Apartment 新建的一个STA套间
MTA Free 进程内的MTA套间
MTA Both 进程内的MTA套间
MTA ""或Single 进程内的主STA套间
MTA Neutral 进程内的NA套间



进程内的主STA套间是进程中第一个调用CoInitialize的线程所关联的套间(即进程中的第一个STA套间)。后面说明为什么还来个进程内的主STA套间。

当是进程外组件时,由主函数调用CoInitializeEx或CoInitialize指定组件所在套间,与上面的相同,CoInitialize代表STA,CoInitializeEx( NULL, COINIT_MULTITHREADED );代表MTA,没有NA。因为NA是COM+提供的,而COM+服务只能提供给进程内服务器,因此只使用上面的注册表项的规则决定DLL组件是否放进NA套间,而没有提供类似CoInitializeEx( NULL, COINIT_NEUTRAL );来处理EXE组件。而且如果可以使用CoInitializeEx(NULL, COINIT_NEUTRAL );将导致调用线程和NA套间相关联了,违背了NA的线程模型,这也是为什么ThreadingModel键在<CLSID>\InprocServer32键下。

跨套间调用

STA线程1创建了一个STA对象,得到接口指针IABCD*,接着它发起STA线程2,并且将IABCD*作为线程参数传入。在线程2中,调用IABCD::Abc()方法,成功或者失败天注定。由于线程2所在的STA套间不同于线程1所在的STA套间,这样线程2就跨套间调用另一个套间的对象了。按照前述的STA规则,IABCD::Abc()应该被转成消息来发送,而如果如上面做法,可以,编译通过,不过运行就不保证了。

COM之所以能够实现前面所说的那些规则(STA、MTA、NA),是因为跨套间调用时,被调用的对象指针是指向一个代理对象,不是组件对象本身。而那个代理对象实现前述的那三个实现算法(转成消息发送,线程切换等),而一般所说的代理/占位对象(Proxy/Stub)等其实都只是指进行汇集工作的代码(后述)。而按照上面直接通过线程参数传入的指针是直接指向对象的,所以将不能实现STA规则,为此COM提供了如下两个函数(还有其他方式,如通过全局接口表GIT)来方便产生代理:CoMarshalInterface和CoUnmarshalInterface(如果在同一进程内的线程间传递接口指针,则可以通过这两个函数来进一步简化代码的编写:CoMarshalInterThreadInterfaceInStream和CoGetInterfaceAndReleaseStream)。
现在重写上面代码,线程1得到IABCD*后,调用CoMarshalInterface得到一个IStream*,然后将IStream*传入线程2,在线程2中,调用CoUnmarshalInterface得到IABCD*,现在这个IABCD*就是指向代理对象的,而不是组件对象了。

因此,前面所说过的所有线程模型的算法都是通过代理对象实现的。要跨套间时,使用CoMarshalInterface将代理对象的CLSID和其与组件对象建立联系的一些必要信息(如组件对象的接口指针)列集(Marshaling)到一个IStream*中,再通过任何线程间通信手段(如全局变量等)将IStream*传到要使用的线程中,再用CoUnmarshalInterface散集(Unmarshaling)出接口以获得指向代理对象的接口指针。因此之所以要获得代理对象的指针是因为想使用COM提供的线程模型(但在COM+中,这不是唯一的理由),如果不想使用大可不必这么麻烦(不过后果自负),并没有强制要求必须那么做。

当线程1和线程2都是MTA时,则可以像最开始说的那样,直接传递IABCD*到线程2中,因为MTA线程模型同意多个线程同时直接调用对象,线程1和线程2在同一个MTA套间中,而那个对象通过某种形式(如ThreadingModel = Free)向COM声明了自己支持MTA线程模型。

而当a.exe的线程1和b.exe的线程2都是MTA时,则依旧需要像上面那样进行接口指针的汇集(列集→传输→散集这个过程)以得到指向代理而非对象的指针,即使线程1和线程2都是在MTA套间中,却是在两个不同的MTA套间中,因此是跨套间调用,需要汇集操作。

汇集代码

前面已经说明了套间的规则都是通过对代理对象而非组件对象发起调用以截取对组件对象的调用由代理对象来实现的。代理对象要和组件对象交互,将方法参数传递给组件对象,需要使用到汇集技术,也就是列集→传输→散集这个过程。

列集(Marshaling)指将信息以某种格式存为流(IStream*)形式的操作;散集(Unmarshaling)则是列集的反操作,将信息从流形式中反还出来;传输则只是流形式的传递操作。

这里经常发生误会。前面的CoMarshalInterface所做的列集,是将代理对象的CLSID及一些持久信息(用于初始化代理对象)格式化为一种格式(网络数据描述——Network Data Representation)后放到一个流对象中,可以通过网络(或其他方式)将这个流对象传递到客户机,由客户通过CoUnmarshalInterface从传来的流对象中反还出代理对象的CLSID和初始化用的一些持久信息,生成代理对象并使用持久信息初始化它以用于汇集操作。这就是发生误会的地方——这里的汇集操作不同于上面的汇集操作,其汇集的是接口方法的参数而不是什么CLSID和一些初始化信息。

因此CoMarshalInterface和CoUnmarshalInterface是用于汇集接口指针的,再准确点应该是用于生成代理对象的。代理对象应由读者自己实现,用于汇集接口方法的参数。一般有两种代理对象的实现方式:自定义汇集和标准汇集。

对于自定义汇集,组件需实现IMarshal接口和一个代理组件(即完全实现真正组件所有接口的一个副本,实现了汇集方法参数及线程模型的规则,也必须实现IMarshal接口),并将这个代理组件在客户机上注册,以保证代理对象的正确生成。注意:如果参数中有接口指针,必须用CoMarshalInterface和CoUnmarshalInterface进行汇集,否则无法实现正确的线程模型,且代理组件是线程模型的实现者,这点组件必须自己保证(如发送消息等)。

对于标准汇集,组件无需实现IMarshal接口及代理组件,代替的,组件则需要为自己生成一个代理/占位组件(Proxy/Stub),其由于可通过MIDL由IDL文件自动生成,效率高,代码的正确性有保证,因而被鼓励使用。COM提供了一个标准代理对象的实现,其通过聚合组件的代理/占位组件以表现出其好像是组件的代理对象。与自定义汇集一样,需要将这个代理/占位组件在客户机上注册以保证代理对象的正确生成。

至于这两种汇集的具体工作机理,由于与本文无关,在此不表,这里仅仅只为消除代理对象和代理/占位组件之间的混淆。

注意:对于将运行于NA套间的组件,由于COM+的强制要求,其必须使用标准汇集进行代理对象的生成而不是自定义汇集(COM+运行时期库重写了标准代理对象来截获对组件对象的调用和其自身的某些特殊处理——如保证NA套间正确工作)。

套间实现规则

  如前面所说,COM的套间机制要成功,必须服务器(组件)、客户和COM运行时期库三方面合力实现,其中有任何一方不按着规矩来,将不能实现套间机制的功能,不过这并不代表什么错误,套间机制不能运作并不代表程序会崩溃,只是不能和其他COM应用兼容而已。

比如:对象中的属性1在设计的算法中肯定不会被两个以上的线程写入,只是会被多个线程同时读出而已,因此不用同步,可以用MTA,但对象的属性2却可能被多个线程写入,因此决定使用STA。从而在客户端,通过前面说的CoMarshalInterface和CoUnmarshalInterface将对象指针传到那个只会写入对象的属性1的线程,其实这时就可以直接将对象指针传到这个线程,而不用想上面那样麻烦(而且增加了效率),但是就破坏了COM的套间规矩了——两个线程可以访问对象,但对象在STA套间中。所以?!!什么事都不会发生,因为已经准确知道这个算法不会捅娄子(线程访问冲突),即使破坏COM的规矩又怎样?!而且组件仍可以和其他客户兼容,因为不按规矩来的是客户,与组件无关。不过如果组件破坏规矩,那么它将不能和每一个客户兼容,但并不代表它和任何客户都不兼容。这里其实就是客户和组件联合起来欺骗了COM运行时期库。

上面的例子只是想帮助读者加深对套间的理解,实际中应该尽量保持和COM规范的兼容性(但不兼容并不代表是错误的)。客户要做的工作前面已经说过了(那两个函数或全局接口表或其他只要正确的方式),下面说明组件应该做的工作。组件可以存在于四个套间中(多了一个主STA套间),所需工作分别如下:

STA 当一个组件是STA时,它必须同步保护全局变量和静态变量,即对全局变量和静态变量的访问应该用临界段或其他同步手段保护,因为操作全局和静态变量的代码可以被多个STA线程同时执行,所以那些代码的地方要进行保护。比如对象计数(注意,不是引用计数),代表当前组件生成的对象个数,当减为零时,组件被卸载。此变量一般被类厂对象使用,还好ATL和MFC已经帮我们实现了缺省类厂,这里一般不用担心,但自定义的全局或静态变量得自己处理。

主STA 与STA唯一的不同是这是傻瓜型的,连静态和全局变量都可以不用线程保护,因为所有不是安全访问静态和全局变量的对象都通过主线程(第一个调用CoInitialize的线程)的消息派送机制运行,因此不安全的访问都被集中到了一个线程的调用中,因而调用被序列化了,也就实现了对静态和全局变量的线程保护。至于为什么是主线程,因为进程要使用STA,则一定会创建主线程,所以一定可以创建主STA。因此主STA并不是什么第四种套间,只是一个STA套间,不过关联的是主线程而已,由于它可以被用作保护静态和全局变量而被单独提出来说明。因此一个进程内也只有一个主STA套间。

MTA 必须对组件中的每个成员和全局及静态变量的访问使用同步手段进行保护,还应考虑线程问题,即不是简单地保护访问即可,还应注意线程导致的错误的操作,最经典的就是IUnknown::Release()。


DWORD IUnknown::Release()
{
DWORD temp = InterlockedDecreament( &m_RefCount );
if( !temp ) // 不能用m_RefCount,原因请自己思考
delete this; // 因此不是只要用原子访问函数保护了m_RefCount的访问就行了
return temp; // 前面对全局变量的保护也和此类似,要考虑线程问题
}

如果读者对自己多线程编程的技术没有信心,建议最好不要编写可以存在于MTA套间的组件,不过就不能获得MTA的高性能了。

在编写MTA时还应该注意到线程亲缘性(thread affinity)。没有线程亲缘性是指没有任何线程范围的成员变量,比如线程局部存储(TLS)、窗口句柄等。也就是说在MTA中不能保存任何记录着TLS内存的指针或窗口句柄,如果保存将没有意义(比如A线程记录的内存空间对B线程来说是无效的,因为TLS构造了一个线程相关的内存空间,就像每个进程都有自己的私有空间)。而不幸地MFC在它的底层运作机制的实现中大量使用了TLS,如模块线程状态、线程状态等。正是由于这个原因,MFC不能编写在MTA中运行的组件。

NA 由于可能会多个线程同时访问NA套间的对象,因此和MTA一样,其不能有线程亲缘性并需要保护每个成员和全局及静态变量。而关于NA的轻量级代理,是由COM+运行时期库生成的,读者完全不用操心(只需将那个组件的ThreadingModel键值赋值为“Neutral”即可)。

前面提到过有一种进程内组件的ThreadingModel键值可以被赋为“Both”,这种组件很像NA,哪个套间都可能直接访问它,但只是可能,而NA组件是可以,这点可以从前面的那个进程内组件所属套间的规则表中看出。这种组件可以支持一种称作自由线程汇集器(FTM——Free Threaded Marshaler)的技术,由于其与本文题目无关,在此不表。当Both的组件使用了自由线程汇集器时,除了满足MTA的要求以外(上面所说的线程安全保护和没有线程相关性),还要记录传进来的接口指针的中立形式(比如IStream*,通过CoMarshallInterface得到),以防止对客户的回调问题。

最后只是提醒一下,有3个STA套间,STA1、STA2和STA3。STA1用CoMarshallInterface得到的IStream*传到STA2中通过CoUnmarshalInterface得到的代理和在STA3中同样通过CoUnmarshalInterface得到的代理不同,不能混用。因为当STA2和STA3调用在STA1的对象时,STA1如果回调(连接点技术就是一种回调)调用者,则STA2和STA3的代理能分别正确的指出需要让哪个线程执行回调操作,即向哪个线程发送消息,因此不能混用。
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 使用html5+canvas+Jquery实现的纯代码连线题Demo

    前端一直是令众等小牛们胆怯的领域,但一旦涉足,技术自然也是蹭蹭蹭的往上涨,荷包也就自然的bingo了。然而在这万千世界加之资料满街撒的时代,却仍然在我们开发过程中总有那么一丢丢的技术点难以找到合适自身的demo,故而引发了众生们的不满和抱怨,于是乎苦逼的从后端到前端…...

    2024/4/14 17:06:11
  2. SQL必知必会学习笔记_1、2课

    内容转载自图书:《SQL必知必会》 第一课 了解SQL 1.1数据库基础 1.1.1数据库1.数据库(database):保存有组织的数据的容器(通常是一个文件或者一组文件)。2.易误用与混淆:人们常用数据库这个术语来代表他们使用的数据库软件。数据库软件应该称为数据库管理系统(DBMS)。…...

    2024/4/5 1:43:46
  3. Elasticsearch高级调优方法论之——根治慢查询!

    1、引言Elasticsearch是非常灵活且功能丰富的搜索引擎,它提供了许多不同查询数据的方法。在实战业务场景中,经常会出现远远低于预期查询速度的慢查询。作为分布式系统的Elasticsearch,可能有各种影响查询性能的因素,包括外部因素,如负载均衡设置,网络延迟(带宽,NIC卡/驱…...

    2024/4/13 1:52:14
  4. HTML5语音播报引发的:关于TTS引擎扩展及修复注意事项

    首先给出HTML5语音播报实例,具体解释请查阅相关文档://html5语音播报 function speak(textToSpeak) {//创建一个 SpeechSynthesisUtterance的实例var utterance = new SpeechSynthesisUtterance();// 设置文本utterance.text = textToSpeak;//增加中文支持utterance.lang = z…...

    2024/5/4 16:12:32
  5. SQL必知必会(2) 2~6课

    参考http://blog.csdn.net/basycia/article/details/52502529 http://blog.csdn.net/dreamworker007/article/details/13161587第2课 检索数据----SELECT 检索一个或者多个列单列,多列,所有列SELECT id/SELECT id,age/SELECT*FROM user;多条SQL语句必须以“ ;”分隔。SQL语…...

    2024/4/13 5:17:11
  6. 数学对计算机的重要性

    首先,应该意识到数学修养的重要性。作为一个优秀的程序员,一定的数学修养是十分重要也是必要的。数学是自然科学的基础,计算机科学实际上是数学的一个分支。计算机理论其实是很多数学知识的融合,软件工程需要图论,密码学需要数论,软件测试需要组合数学,计算机程序的编制…...

    2024/4/13 1:52:14
  7. 《SQL必知必会》| 第21课 使用游标 学习笔记

    第21课 使用游标 这一课将讲授什么是游标,如何使用游标。 21.1 游标SQL检索操作返回一组称为结果集的行,这组返回的行都是与SQL语句相匹配的行(零行或多行)。有时,需要在检索出来的行中前进或后退一行或多行,这就是游标的用途所在。游标cursor是一个存储在DBMS服务器上的数…...

    2024/5/4 18:07:21
  8. 抽象类和模板模式

    一模板模式的简单规则抽象父类可以只定义需要使用的某些方法,把不能实现的部分抽象成抽象方法,留给其子类去实现。父类中可能包含需要调用其它系列方法的方法,这些被调用的方法既可以由父类实现,也可以由其子类实现。父类里提供的方法只定义了一个通用算法,其实现也许并不…...

    2024/4/8 20:20:49
  9. SQL必知必会(三) 联结表、创建高级联结、组合查询

    十二、联结表关系表关系表的设计就是要把信息分解成多个表,一类数据一个表,各表通过某些共同的值互相关联。 这本书的数据库分成5个,5个表之间通过m某些id连接起来。创建联结SELECT vend_name , prod_name ,prod_price FROM Vendors , Products WHERE Vendors.vend_id = Pro…...

    2024/5/1 12:48:57
  10. 技术管理者眼中的行业

    行业特性在不同人员眼里具有不同的表现形式,对于技术管理者而言,自然也有其对行业的理解模型。尤其在当下软件产品处于互联网的大背景下,各个行业在自身特性上也表现出一定的共性。1. 互联网背景下的行业与软件产品特征我们处在一个互联网空前繁荣的时代,随着“互联网+”概…...

    2024/4/13 1:53:20
  11. SQL必知必会 读书笔记 20160413

    DISTINCT检索不同的值 select 如果不明确规定排序顺序看,则不应该假定检索出的数据的顺序有任何意义 order by 要放在最后 使用通配符进行搜索: % _ 速度会慢,尽量不要使用 TRIM() 去掉字符串左右两端空格,LTRIM()左边 RTRIM()右边 AS 别名(导出列) 日期…...

    2024/4/15 15:37:19
  12. 蓝鸥零基础学习HTML5第八讲 样式布局二

    蓝鸥零基础学习HTML5第八讲 样式布局二1.定位的属性及特性<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>#box1 {width:400px;height:400px;border:10px solid red;margin:0 auto;pos…...

    2024/5/2 20:14:29
  13. SQL必知必会--中级篇(一)

    前两篇文章已经回顾了最基础的部分,从这篇开始回顾一些进阶知识。本篇包含知识点如图:假设有两张表:student(sno,name,sex,age,class,addr) ,sno为主键grade(id,sno,Math,Chinses,English),id为主键以下sql语句,基于mysql数据库编写一、组合查询(union)union : 合并两个…...

    2024/4/21 12:06:39
  14. 微信应用号开发必备技能都在这里了啦!

    “微信应用号”就像平地里炸响的一声春雷,在互联网圈内炸开了锅,小代码小程序即将成为主流,H5迎来了自己的第二春。废话少说,关于微信应用号开发技能,你都掌握了吗?没掌握的看这里,干货全在这儿了!下载中心:《HTML 5 从入门到精通》-中文学习教程http://down.51cto.co…...

    2024/4/13 21:24:30
  15. 选址问题综述

    〔转自马云峰〕现代选址研究起于 1909 年,当时 Alfred Weber 为解决如何为单个仓库选址使得仓库到多个顾客间的总距离最小的问题,他在欧氏空间里建立了一个 1-中位问题模型,就是著名的 Weber 问题。 1)基本选址问题 (1)P-中位问题(p-median problems)  P-中位问题是研…...

    2024/4/20 4:20:26
  16. SQL 必知必会 Chapter 17 —— 创建和操纵表

    typora-copy-images-to: SQL 必知必会 文章目录typora-copy-images-to: SQL 必知必会SQL 必知必会 Chapter 17 创建和操纵表17.1 创建和操纵17.2 更新表17.3 删除表17.4 重命名表17.5 小结 SQL 必知必会 Chapter 17 创建和操纵表 17.1 创建和操纵多数DBMS 都具有交互式创建和管…...

    2024/4/13 1:53:05
  17. 蓝鸥零基础学习HTML5第九讲 兼容性一

    蓝鸥零基础学习HTML5第九讲 兼容性一1.兼容性到底是什么玩意<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>#box {width:400px;}.left {width:200px;background: red;height:300px;fl…...

    2024/4/8 20:20:40
  18. 数学建模课程常用英语词汇(专业术语)

    数学建模课程常用英语词汇(专业术语) 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 数学建模 mathematical modeling 数学模型 mathematical model 初等数学模型 Elementary mathematics model 微分方程模型 differential equation model 线性代数…...

    2024/4/20 12:42:29
  19. 【SQL 必知必会】性能篇 04.深入浅出索引(一)

    索引在SQL优化中占很大的比重,好的索引能提高查询效率。索引好比书本的目录,通过目录我们可以在海量的数据中很快的定位到要查找的内容,如果不加索引,则是通过表一个个扫描的,查询效率极低,但是加了索引一定好么?什么情况下我们不使用索引呢?我们常见的索引类型都有哪些…...

    2024/4/20 13:35:06
  20. 蓝鸥零基础学习HTML5第九讲 兼容性二

    蓝鸥零基础学习HTML5第九讲 兼容性二1.兼容性4<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>p {width:100px;height:100px;background: red;}</style></head><body&g…...

    2024/4/20 4:50:37

最新文章

  1. 双指针 Leetcode 15 三数之和

    双指针 Leetcode 15 三数之和 Leetcode 15 学习记录自代码随想录 要点&#xff1a;1、如果用哈希表则在处理输出数组去重时较复杂&#xff1b; 2、双指针法&#xff0c;双指针法使用前要对数组排序。 class Solution { public:vector<vector<int>> threeSum(ve…...

    2024/5/4 19:56:49
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. java实体中返回前端的double类型四舍五入(格式化)

    根据业务&#xff0c;需要通过后端给前端返回部分double类型的数值&#xff0c;一般需要保留两位小数&#xff0c;使用jackson转换对象 package com.ruoyi.common.core.config; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.Json…...

    2024/5/1 8:51:46
  4. Webpack部署本地服务器

    Webpack部署本地服务器 目录 Webpack部署本地服务器目的认识模块热替换&#xff08;HMR&#xff09;什么是 HMRHMR 通过如下几种方式, 来提高开发的速度如何使用 HMRhost 配置 目的 完成自动编译 常用方式: webpack-dev-server webpack-dev-server 是一个用于开发环境的 Web 服…...

    2024/5/4 14:29:02
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/2 16:16:39
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/3 23:10:03
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/4 18:20:48
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/4 2:59:34
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57