COM中的线程形式,COM线程模型的作为

原文:https://msdn.microsoft.com/library/ms809971.aspx

 

Behavior of the COM Threading Models

COM线程模型的一言一动

Before any thread can create or manipulate COM objects, it must perform
some preliminary initialization to establish its relationship with the
COM library. As a result of this process, COM creates an apartment
appropriate for the initialization routine: CoInitialize creates a
single-threaded apartment (STA), whereas CoInitializeEx with the
COINIT_MULTITHREADED flag produces a multi-threaded apartment.
The CoInitialize/CoInitializeEx call does not initialize the COM
library globally, only the calling thread’s use of it, so it’s important
to remember that this initialization should be done on a per-thread
basis. This is typically done early in a thread’s work function
(ThreadProc).

在任何线程能制造或操作COM对象前,必得举行一些预最初化来创设进度与COM库之间的连日。该操作的结果:COM创立了多个“公寓”适用于初始化例程;CoInitialize用来创造STA,用COINIT_MULTITHREADED标记的CoInitializeEx用于创建MTA。CoInitializeCoInitializeEx调用不可能在大局初步化COM库,只有调用的线程能开端化COM对象。由此,应该记住开首化必需在线程的创始阶段产生。经常应当早于ThreadProc方法。

A single-threaded apartment is associated with the thread that created
it, and only that specific thread may execute calls on objects within
the apartment. By contrast, a multi-threaded apartment is not associated
with any particular thread. It may be called concurrently by any number
of threads and objects within it and should subsequently protect their
member data.

三个STA与创立该饭馆的线程关联,独有一定的线程有比较大可能率施行STA创立的COM对象的调用。与此差异,MTA不与其余特定的线程关联。MTA的COM对象的调用能够被多线程并发的管理,並且由线程自身保障数据的安全。

Communication between apartments is done via marshaling, a generic
abstraction for passing data across thread or process boundaries.
Because calls to single-threaded apartments can only be executed on the
thread that created them, other threads that wish to use an object
within this apartment marshal the call to the apartment’s thread and let
the apartment thread execute the call. The apartment thread then
marshals the return value back to the calling thread.

公寓见的通讯通过封送情势,封送是一个通用的虚幻用于传递数据穿过线程或进程的边界,由于调用STA创制的COM对象只好被创建该应接所的线程实践,其余线程想要使用在STA创立的(COM)对象可以封送这些调用到创制STA的线程,然后让该线程实行那个调用。STA线程然后封送那些调用再次来到调用线程。

Choosing the threading model for an object depends on the object’s
function. An object that does extensive I/O might support free-threading
to provide maximum response to clients by allowing interface calls
during I/O latency. On the other hand, an object that interacts with the
user might support apartment threading to synchronize incoming COM calls
with its window operations.

 

It is easier to support apartment threading in single-threaded
apartments because COM provides synchronization on a per-call basis.
Supporting free-threading is more difficult because the object must
implement synchronization; however, response to clients may be better
because synchronization can be implemented for smaller sections of
code.  

Processes, Threads, and
Apartments

process is a collection of virtual memory space, code, data, and
system resources. A thread is code that is to be serially executed
within a process. A processor executes threads, not processes, so each
application has at least one process, and a process always has at least
one thread of execution, known as the primary thread. A process can have
multiple threads in addition to the primary thread.

Processes communicate with one another through messages, using
Microsoft’s Remote Procedure Call (RPC) technology to pass information
to one another. There is no difference to the caller between a call
coming from a process on a remote machine and a call coming from another
process on the same machine.

When a thread begins to execute, it continues until it is killed or
until it is interrupted by a thread with higher priority (by a user
action or the kernel’s thread scheduler). Each thread can run separate
sections of code, or multiple threads can execute the same section of
code. Threads executing the same block of code maintain separate stacks.
Each thread in a process shares that process’s global variables and
resources.

The thread scheduler determines when and how often to execute a thread,
according to a combination of the process’s priority class attribute and
the thread’s base priority. You set a process’s priority class attribute
by calling the SetPriorityClass.aspx) function
, and you set a thread’s base priority with a call to SetThreadPriority.aspx).

Multithreaded applications must avoid two threading
problems: deadlocks and races. A deadlock occurs when each thread is
waiting for the other to do something. The COM call control helps
prevent deadlocks in calls between objects. A race condition occurs when
one thread finishes before another on which it depends, causing the
former to use an uninitialized value because the latter has not yet
supplied a valid one. COM supplies some functions specifically designed
to help avoid race conditions in out-of-process servers. (See Out-of-Process Server Implementation
Helpers
.aspx).)

The Apartment and the COM Threading Architecture

While COM supports the single-thread-per-process model prevalent before
the introduction of multiple threads of execution, writing code to take
advantage of multiple threads makes it possible to create more efficient
applications than ever before by allowing a thread, while it waits for
some time-consuming operation to complete, to allow another thread to be
executed.

Note   Using multiple threads is not a guarantee of better
performance. In fact, because thread factoring is a difficult problem,
using multiple threads often causes performance problems. The key is to
use multiple threads only if you are very sure of what you are doing.

In general, the simplest way to view the COM threading architecture is
to think of all the COM objects in the process as divided into groups
called apartments. A COM object lives in exactly one apartment, in the
sense that its methods can legally be directly called only by a thread
that belongs to that apartment. Any other thread that wants to call the
object must go through a proxy.

There are two types of apartments: single-threaded
apartments
.aspx),
and multithreaded
apartments
.aspx).

  • Single-threaded apartments consist of exactly one thread, so all COM
    objects that live in a single-threaded apartment can receive method
    calls only from the one thread that belongs to that apartment. All
    method calls to a COM object in a single-threaded apartment are
    synchronized with the windows message queue for the single-threaded
    apartment’s thread. A process with a single thread of execution is
    simply a special case of this model.
  • Multithreaded apartments consist of one or more threads, so all COM
    objects that live in an multithreaded apartment can receive method
    calls directly from any of the threads that belong to the
    multithreaded apartment. Threads in a multithreaded apartment use a
    model called free-threading. Calls to COM objects in a
    multithreaded apartment are synchronized by the objects themselves.

Note   For a description of communication between single-threaded
apartments and multithreaded apartments within the same process,
see Single-Threaded and Multithreaded
Communication
.aspx).

A process can have zero or more single-threaded apartments and zero or
one multithreaded apartment.

In a process, the main apartment is the first to be initialized. In a
single-threaded process, this is the only apartment. Call parameters are
marshaled between apartments, and COM handles the synchronization
through messaging. If you designate multiple threads in a process to be
free-threaded, all free threads reside in a single apartment, parameters
are passed directly to any thread in the apartment, and you must handle
all synchronization. In a process with both free-threading and apartment
threading, all free threads reside in a single apartment and all other
apartments are single-threaded apartments. A process that does COM work
is a collection of apartments with, at most, one multithreaded apartment
but any number of single-threaded apartments.

The threading models in COM provide the mechanism for clients and
servers that use different threading architectures to work together.
Calls among objects with different threading models in different
processes are naturally supported. From the perspective of the calling
object, all calls to objects outside a process behave identically, no
matter how the object being called is threaded. Likewise, from the
perspective of the object being called, arriving calls behave
identically, regardless of the threading model of the caller.

Interaction between a client and an out-of-process object is
straightforward, even when they use different threading models because
the client and object are in different processes. COM, interposed
between the client and the server, can provide the code for the
threading models to interoperate, using standard marshaling and RPC. For
example, if a single-threaded object is called simultaneously by
multiple free-threaded clients, the calls will be synchronized by COM by
placing corresponding window messages in the server’s message queue. The
object’s apartment will receive one call each time it retrieves and
dispatches messages. However, some care must be taken to ensure that
in-process servers interact properly with their clients. (See In-Process Server Threading
Issues
.aspx).)

The most important issue in programming with a multithreaded model is to
make your code thread-safe so that messages intended for a particular
thread go only to that thread and access to threads is protected.

In-Process Server Threading
Issues

An in-process server does not call CoInitialize.aspx), CoInitializeEx.aspx),
or OleInitialize.aspx) to
mark its threading model. For thread-aware DLL-based or in-process
objects, you need to set the threading model in the registry. The
default model when you do not specify a threading model is
single-thread-per-process. To specify a model, you add
theThreadingModel value to the InprocServer32.aspx) key
in the registry.

DLLs that support instantiation of a class object must implement and
export the functions DllGetClassObject.aspx)and DllCanUnloadNow.aspx).
When a client wants an instance of the class the DLL supports, a call
toCoGetClassObject.aspx) (either
directly or through a call to CoCreateInstance.aspx))
calls DllGetClassObject to get a pointer to its class object when
the object is implemented in a DLL. DllGetClassObject should
therefore be able to give away multiple class objects or a single
thread-safe object (essentially just usingInterlockedIncrement.aspx)/InterlockedDecrement.aspx) on
their internal reference counts).

As its name implies, DllCanUnloadNow.aspx) is
called to determine whether the DLL that implements it is in use,
enabling the caller to safely unload it if it is not. Calls to CoFreeUnusedLibraries.aspx) from
any thread always route through the main apartment’s thread to
call DllCanUnloadNow.

Like other servers, in-process servers can be single-threaded,
apartment-threaded, or free-threaded. These servers can be used by any
OLE client, regardless of the threading model used by that client.

All combinations of threading model interoperability are allowed between
clients and in-process objects. Interaction between a client and an
in-process object that use different threading models is exactly like
the interaction between clients and out-of-process servers. For an
in-process server, when the threading model of the client and in-process
server differ, COM must interpose itself between the client and the
object.

When an in-process object that supports the single-threaded model is
called simultaneously by multiple threads of a client, COM cannot allow
the client threads to directly access the object’s interface—the object
was not designed for such access. Instead, COM must ensure that calls
are synchronized and are made only by the client thread that created the
object. Therefore, COM creates the object in the client’s main apartment
and requires all the other client apartments to access the object by
using proxies.

When a free-threaded apartment (multithreaded apartment model) in a
client creates an apartment-threaded in-process server, COM spins up a
single-threaded apartment model “host” thread in the client. This host
thread will create the object, and the interface pointer will be
marshaled back to the client’s free-threaded apartment. Similarly, when
a single-threaded apartment in an apartment-model client creates a
free-threaded in-process server, COM spins up a free-threaded host
thread (multithreaded apartment on which the object will be created and
then marshaled back to the client single-threaded apartment).

Note   In general, if you design a custom interface on an in-process
server, you should also provide the marshaling code for it so that COM
can marshal the interface between client apartments.

COM helps protect access to objects provided by a single-threaded DLL by
requiring access from the same client apartment in which they were
created. In addition, all of the DLL entry points (like DllGetClassObject.aspx) andDllCanUnloadNow.aspx))
and global data should always be accessed by the same apartment. COM
creates such objects in the main apartment of the client, giving the
main apartment direct access to the object’s pointers. Calls from the
other apartments use interthread marshaling to go from the proxy to the
stub in the main apartment and then to the object. This allows COM to
synchronize calls to the object. Interthread calls are slow, so it is
recommended that these servers be rewritten to support multiple
apartments.

Like a single-threaded in-process server, an object provided by an
apartment model DLL must be accessed by the same client apartment from
which it was created. However, objects provided by this server can be
created in multiple apartments of the client, so the server must
implement its entry points (like DllGetClassObject.aspx) andDllCanUnloadNow.aspx))
for multithreaded use. For example, if two apartments of a client try to
create two instances of the in-process object
simultaneously, DllGetClassObject can be called simultaneously by
both apartments.DllCanUnloadNow must be written so that the DLL does
not unload while code is still executing in the DLL.

If the DLL provides only one instance of the class factory to create all
the objects, the class factory implementation must also be designed for
multithreaded use, because it will be accessed by multiple client
apartments. If the DLL creates a new instance of the class factory each
time DllGetClassObject.aspx) is
called, the class factory need not be thread-safe.

Objects created by the class factory need not be thread-safe. Once
created by a thread, the object is always accessed through that thread
and all calls to the object are synchronized by COM. The apartment model
apartment of a client that creates this object will get a direct pointer
to the object. Client apartments that are different from the apartment
in which the object was created must access the object through proxies.
These proxies are created when the client marshals the interface between
its apartments.

When an in-process DLL ThreadingModel value is set to “Both”, an
object provided by this DLL can be created and used directly (without a
proxy) in single-threaded or multithreaded client apartments. However,
it can be used directly only within the apartment in which it was
created. To give the object to any other apartment, the object must be
marshaled. The DLL object must implement its own synchronization and can
be accessed by multiple client apartments at the same time.

To speed performance for free-threaded access to in-process DLL objects,
COM provides theCoCreateFreeThreadedMarshaler.aspx) function.
This function creates a free-threaded marshaling object that can be
aggregated with an in-process server object. When a client apartment in
the same process needs access to an object in another apartment,
aggregating the free-threaded marshaler provides the client with a
direct pointer to the server object, rather than to a proxy, when the
client marshals the object’s interface to a different apartment. The
client does not need to do any synchronization. This works only within
the same process—standard marshaling is used for a reference to the
object that is sent to another process.

An object provided by an in-process DLL that supports only free
threading is a free-threaded object. It implements its own
synchronization and can be accessed by multiple client threads at the
same time. This server does not marshal interfaces between threads, so
this server can be created and used directly (without a proxy) only by
multithreaded apartments in a client. Single-threaded apartments that
create it will access it through a proxy.

 

Single-Threaded
Apartments

Using single-threaded apartments (the apartment model process) offers a
message-based paradigm for dealing with multiple objects running
concurrently. It enables you to write more efficient code by allowing a
thread, while it waits for some time-consuming operation to complete, to
allow another thread to be executed.

Each thread in a process that is initialized as an apartment model
process, and that retrieves and dispatches window messages, is a
single-threaded apartment thread. Each thread lives within its own
apartment. Within an apartment, interface pointers can be passed without
marshaling, and therefore, all objects in one single-threaded apartment
thread communicate directly.

A logical grouping of related objects that all execute on the same
thread, and therefore must have synchronous execution, could live on the
same single-threaded apartment thread. However, an apartment model
object cannot reside on more than one thread. Calls to objects in other
processes must be made within the context of the owning process, so
distributed COM switches threads for you automatically when you call on
a proxy.

The interprocess and interthread models are similar. When it is
necessary to pass an interface pointer to an object in another apartment
(on another thread) within the same process, you use the same marshaling
model that objects in different processes use to pass pointers across
process boundaries. By getting a pointer to the standard marshaling
object, you can marshal interface pointers across thread boundaries
(between apartments) in the same way you do between processes.
(Interface pointers must be marshaled when passed between apartments.)

Rules for single-threaded apartments are simple, but it is important to
follow them carefully:

  • Every object should live on only one thread (within a
    single-threaded apartment).
  • Initialize the COM library for each thread.
  • Marshal all pointers to objects when passing them between
    apartments.
  • Each single-threaded apartment must have a message loop to handle
    calls from other processes and apartments within the same process.
    Single-threaded apartments without objects (client only) also need a
    message loop to dispatch the broadcast messages that some
    applications use.
  • DLL-based or in-process objects do not call the COM initialization
    functions; instead, they register their threading model with
    the ThreadingModel named-value under the InprocServer32.aspx) key
    in the registry. Apartment-aware objects must also write DLL entry
    points carefully. There are special considerations that apply to
    threading in-process servers. For more information, see In-Process Server Threading
    Issues
    .aspx).

While multiple objects can live on a single thread, no apartment model
object can live on more than one thread.

Each thread of a client process or out-of-process server must
call CoInitialize.aspx),
or call CoInitializeEx.aspx) and
specify COINIT_APARTMENTTHREADED for the dwCoInit parameter. The main
apartment is the thread that calls CoInitializeEx first. For
information on in-process servers, see In-Process Server Threading
Issues
.aspx).

All calls to an object must be made on its thread (within its
apartment). It is forbidden to call an object directly from another
thread; using objects in this free-threaded manner could cause problems
for applications. The implication of this rule is that all pointers to
objects must be marshaled when passed between apartments. COM provides
the following two functions for this purpose:

These functions wrap calls to CoMarshalInterface.aspx) and CoUnmarshalInterface.aspx) functions,
which require the use of the MSHCTX_INPROC flag.

In general, the marshaling is accomplished automatically by COM. For
example, when passing an interface pointer as a parameter in a method
call on a proxy to an object in another apartment, or when
callingCoCreateInstance.aspx),
COM does the marshaling automatically. However, in some special cases,
where the application writer is passing interface pointers between
apartments without using the normal COM mechanisms, the writer must
handle the marshaling manually.

If one apartment (Apartment 1) in a process has an interface pointer and
another apartment (Apartment 2) requires its use, Apartment 1 must
call CoMarshalInterThreadInterfaceInStream.aspx) to
marshal the interface. The stream that is created by this function is
thread-safe and must be stored in a variable that is accessible by
Apartment 2. Apartment 2 must pass this stream to CoGetInterfaceAndReleaseStream.aspx) to
unmarshal the interface and will get back a pointer to a proxy through
which it can access the interface. The main apartment must remain alive
until the client has completed all COM work (because some in-process
objects are loaded in the main apartment, as described in In-Process Server Threading
Issues
.aspx)).
After one object has been passed between threads in this manner, it is
very easy to pass interface pointers as parameters. That way,
distributed COM does the marshaling and thread switching for the
application.

To handle calls from other processes and apartments within the same
process, each single-threaded apartment must have a message loop. This
means that the thread’s work function must have a
GetMessage/DispatchMessage loop. If other synchronization primitives are
being used to communicate between threads, the MsgWaitForMultipleObjects.aspx) function
can be used to wait both for messages and for thread synchronization
events. The documentation for this function has an example of this sort
of combination loop.

COM creates a hidden window using the Windows class
“OleMainThreadWndClass” in each single-threaded apartment. A call to an
object is received as a window message to this hidden window. When the
object’s apartment retrieves and dispatches the message, the hidden
window will receive it. The window procedure will then call the
corresponding interface method of the object.

When multiple clients call an object, the calls are queued in the
message queue and the object will receive a call each time its apartment
retrieves and dispatches messages. Because the calls are synchronized by
COM and the calls are always delivered by the thread that belongs to the
object’s apartment, the object’s interface implementations need not
provide synchronization. Single-threaded apartments can
implement IMessageFilter.aspx)to
permit them to cancel calls or receive window messages when necessary.

The object can be reentered if one of its interface method
implementations retrieves and dispatches messages or makes an ORPC call
to another thread, thereby causing another call to be delivered to the
object (by the same apartment). OLE does not prevent reentrancy on the
same thread, but it can help provide thread safety. This is identical to
the way in which a window procedure can be reentered if it retrieves and
dispatches messages while processing a message. However, calling an
out-of-process single-threaded apartment server that calls another
single-threaded apartment server will allow the first server to be
reentered.

Accessing Interfaces Across
Apartments

COM provides a way for any apartment in a process to get access to an
interface implemented on an object in any other apartment in the
process. This is done through the IGlobalInterfaceTable.aspx) interface.
This interface has three methods, which allow you to do the following:

  • Register an interface as a global (processwide) interface.
  • Get a pointer to that interface from any other apartment through a
    cookie.
  • Revoke the global registration of an interface.

The IGlobalInterfaceTable.aspx) interface
is an efficient way for a process to store an interface pointer in a
memory location that can be accessed from multiple apartments within the
process, such as process-wide variables andagile objects
(free-threaded, marshaled objects) containing interface pointers to
other objects.

An agile object is unaware of the underlying COM infrastructure in which
it runs; in other words, what apartment, context, and thread it is
executing on. The object may be holding on to interfaces that are
specific to an apartment or context. For this reason, calling these
interfaces from wherever the agile component is executing might not
always work properly. The global interface table avoids this problem by
guaranteeing that a valid proxy (or direct pointer) to the object is
used, based on where the agile object is executing.

Note   The global interface table is not portable across process or
machine boundaries, so it cannot be used in place of the normal
parameter-passing mechanism.

When to Use the Global
Interface Table

If you are unmarshaling an interface pointer multiple times between
apartments in a process, you might use theIGlobalInterfaceTable.aspx) interface.
With other techniques, you would have to remarshal each time.

Note   If the interface pointer is unmarshaled only once, you may
want to use theCoMarshalInterThreadInterfaceInStream.aspx) function.
It can also be used to pass an interface pointer from one thread to
another thread in the same process.

The IGlobalInterfaceTable.aspx) interface
also makes another previously difficult problem simpler for the
programmer. This problem occurs when the following conditions apply:

  • An in-process agile object aggregates the free-threaded marshaler.
  • This same agile object also holds (as member variables) interface
    pointers to other objects that are not agile and do not aggregate
    the free-threaded marshaler.

In this situation, if the outer object gets marshaled to another
apartment and the application calls on it, and the object tries to call
on any of its member variable interface pointers that are not
free-threaded or are proxies to objects in other apartments, it might
get incorrect results or the error RPC_E_WRONG_THREAD. This error
occurs because the inner interface is designed to be callable only from
the apartment in which it was first stored in the member variable.

To solve this problem, the outer object aggregating the free-threaded
marshaler should callIGlobalInterfaceTable::RegisterInterfaceInGlobal.aspx) on
the inner interface and store the resulting cookie in its member
variable, instead of storing the actual interface pointer. When the
outer object wants to call on an inner object’s interface pointer, it
should call IGlobalInterfaceTable::GetInterfaceFromGlobal.aspx),
use the returned interface pointer, and then release it. When the outer
object goes away, it should callIGlobalInterfaceTable::RevokeInterfaceFromGlobal.aspx) to
remove the interface from the global interface table

Use the following call to create the global interface table object and
get a pointer to IGlobalInterfaceTable.aspx):

HRESULT hr;
hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable,
                 NULL,
                 CLSCTX_INPROC_SERVER,
                 IID_IGlobalInterfaceTable,
                 (void **)&gpGIT);
if (hr != S_OK) {
  exit(0); // Handle errors here.
}

Note   When creating the global interface table object using the
preceding call, it is necessary to link to the library uuid.lib. This
will resolve the external symbols CLSID_StdGlobalInterfaceTable and
IID_IGlobalInterfaceTable.

There is a single instance of the global interface table per process, so
all calls to this function in a process return the same instance.

After the call to the CoCreateInstance.aspx) function,
register the interface from the apartment in which it resides with a
call to the RegisterInterfaceInGlobal.aspx) method.
This method supplies a cookie that identifies the interface and its
location. An apartment seeking a pointer to this interface then calls
the GetInterfaceFromGlobal.aspx)method
with this cookie, and the implementation then supplies an interface
pointer to the calling apartment. To revoke the interface’s global
registration, any apartment can call the RevokeInterfaceFromGlobal.aspx) method.

A simple example of using IGlobalInterfaceTable.aspx) would
be when you want to pass an interface pointer on an object in a
single-threaded apartment (STA) to a worker thread in another apartment.
Rather than having to marshal it into a stream and pass the stream to
the worker thread as a thread parameter,IGlobalInterfaceTable allows
you simply to pass a cookie.

When you register the interface in the global interface table, you get a
cookie that you can use instead of passing the actual pointer (whenever
you need to pass the pointer), either to a nonmethod parameter that is
going to another apartment (as a parameter to ThreadProc.aspx) through CreateThread.aspx))
or to in-process memory accessible outside your apartment.

Care is required because using global interfaces places the extra burden
on the programmer of managing problems such as race conditions and
mutual exclusion, which are associated with accessing global state from
multiple threads simultaneously.

COM provides a standard implementation of the IGlobalInterfaceTable.aspx) interface.
It is highly recommended that you use this standard implementation
because it provides complete thread-safe functionality.

Multithreaded Apartments

In a multithreaded apartment model, all the threads in the process that
have been initialized as free-threaded reside in a single apartment.
Therefore, there is no need to marshal between threads. The threads need
not retrieve and dispatch messages because COM does not use window
messages in this model.

Calls to methods of objects in the multithreaded apartment can be run on
any thread in the apartment. There is no serialization of calls; many
calls may occur to the same method or to the same object simultaneously.
Objects created in the multithreaded apartment must be able to handle
calls on their methods from other threads at any time.

Because calls to objects are not serialized in any way, multithreaded
object concurrency offers the highest performance and takes the best
advantage of multiprocessor hardware for cross-thread, cross-process,
and cross-machine calling. This means, however, that the code for
objects must provide synchronization in their interface implementations,
typically through the use of synchronization primitives such as event
objects, critical sections, mutexes, or semaphores, which are described
later in this section. In addition, because the object doesn’t control
the lifetime of the threads that are accessing it, no thread-specific
state may be stored in the object (in thread local storage).

Following are some important considerations regarding synchronization
for multithreaded apartments:

  • COM provides call synchronization for single-threaded apartments
    only.
  • Multithreaded apartments do not receive calls while making calls (on
    the same thread).
  • Multithreaded apartments cannot make input-synchronized calls.
  • Asynchronous calls are converted to synchronous calls in
    multithreaded apartments.
  • The message filter is not called for any thread in a multithreaded
    apartment.

To initialize a thread as free-threaded, call CoInitializeEx.aspx),
specifying COINIT_MULTITHREADED. For information on in-process server
threading, see In-Process Server
Threading
Issues
.aspx).

Multiple clients can simultaneously call, from different threads, an
object that supports free-threading. In free-threaded out-of-process
servers, COM, through the RPC subsystem, creates a pool of threads in
the server process and a client call (or multiple client calls) can be
delivered by any of these threads at any time. An out-of-process server
must also implement synchronization in its class factory. Free-threaded,
in-process objects can receive direct calls from multiple threads of the
client.

The client can do COM work in multiple threads. All threads belong to
the same multithreaded apartment. Interface pointers are passed directly
from thread to thread within a multithreaded apartment, so interface
pointers are not marshaled between its threads. Message filters
(implementations of IMessageFilter.aspx))
are not used in multithreaded apartments. The client thread will suspend
when it makes a COM call to out-of-apartment objects and will resume
when the call returns. Calls between processes are still handled by RPC.

Threads initialized with the free-threaded model must implement their
own synchronization. As mentioned earlier in this section, Windows
enables this implementation through the following synchronization
primitives:

  • Event objects provide a way of signaling one or more threads that an
    event has occurred. Any thread within a process can create an event
    object. A handle to the event is returned by the event-creating
    function, CreateEvent.aspx).
    Once an event object has been created, threads with a handle to the
    object can wait on it before continuing execution.
  • Critical sections are used for a section of code that requires
    exclusive access to some set of shared data before it can be
    executed and that is used only by the threads within a single
    process. A critical section is like a turnstile through which only
    one thread at a time may pass, working as follows:

    • To ensure that no more than one thread at a time accesses shared
      data, a process’s primary thread allocates a global
      CRITICAL_SECTION data structure and initializes its members. A
      thread entering a critical section calls the EnterCriticalSection.aspx) function
      and modifies the data structure’s members.
    • A thread attempting to enter a critical section calls EnterCriticalSection.aspx) which
      checks to see whether the CRITICAL_SECTION data structure has
      been modified. If so, another thread is currently in the
      critical section and the subsequent thread is put to sleep. A
      thread leaving a critical section calls LeaveCriticalSection.aspx),
      which resets the data structure. When a thread leaves a critical
      section, the system wakes one of the sleeping threads, which
      then enters the critical section.
  • Mutexes performs the same function as a critical section, except
    that the mutex is accessible to threads running in different
    processes. Owning a mutex object is like having the floor in a
    debate. A process creates a mutex object by calling the CreateMutex.aspx) function,
    which returns a handle. The first thread requesting a mutex object
    obtains ownership of it. When the thread has finished with the
    mutex, ownership passes to other threads on a first-come,
    first-served basis.
  • Semaphores are used to maintain a reference count on some available
    resource. A thread creates a semaphore for a resource by calling
    the CreateSemaphore.aspx) function
    and passing a pointer to the resource, an initial resource count,
    and the maximum resource count. This function returns a handle. A
    thread requesting a resource passes its semaphore handle in a call
    to the WaitForSingleObject.aspx)function.
    The semaphore object polls the resource to determine whether it is
    available. If so, the semaphore decrements the resource count and
    wakes the waiting thread. If the count is zero, the thread remains
    asleep until another thread releases a resource, causing the
    semaphore to increment the count to one.

谈起COM的线程方式,实际上指的是多少个方面,二个是客商程序的线程形式,一个是组件所支撑的线程形式。客商程序的线程情势独有二种,单线程公寓(STA)和八线程公寓(MTA)。组件所支撑的线程形式有二种:Single(单线程)、Apartment(STA)、Free(MTA)、Both(STA+MTA)。

  1、公寓只是个逻辑上的定义。一个STA只可以分包贰个线程,叁个MTA能够饱含四个线程。八个历程能够分包多少个STA,但只好有三个MTA。MTA中各线程能够相互的调用本公寓内实例化的零件,而不须求进行调解。跨公寓调用组件实例必须要开展调节。(除非动用了随机线程调解器)

  2、顾客程序的线程是在调用CoInitializeEx()时间调控制顾客线程的类别的。若是以参数COINIT_APARTMENTTHREADED调用,则会成立三个STA公寓,客商线程包含在那一个公寓里。纵然以参数COINIT_MULTITHREADED调用,则创造三个MTA公寓,把线程到场到那个MTA中;假使经过内已经有了贰个MTA,则不创建新的MTA,只把线程出席到已有的MTA。注意各类线程都必得调用CoInitializeEx()才具动用COM组件。

  3、线程最重视的是同步难点。STA是通过窗口语资源新闻息队列来缓和那么些主题素材的。当客商线程以COINIT_APARTMENTTHREADED调用CoInitializeEx()时,将为会该STA创设叁个有所奥莱MainThreadWndClass窗口类的蕴涵窗口。全数对在那一个公寓中确立的COM对象方法的调用都将都放置这一个带有窗口的消息队列中。所以每壹个与STA相关联的线程必需用GetMessage、DispatchMessage或类似措施来平均分摊窗口语资源新闻息。MTA内各线程可并行调用同一个零件对象的实例,进而不保险安全性,所以实现协同访谈的职责就落在了组件身上。注意,STA的一路是公寓级的,正是说对客栈内分裂组件的走访都要放权同二个新闻队列中,对一个实例的方法调用会潜濡默化对其余实例的调用,所以并发程度极低。

  4、在不相同公寓间传递接口指针须求求经过调治。那关键依然为着共同对组件的调用。通过Co马尔斯halInterThreadInterfaceInStream和CoGetInterfaceAndReleaseStream实现。很轻巧。

  5、Single型组件很优秀,它只可以在二个单纯的线程中实践。首先要证实的是一个进度中首先个以COINIT_APARTMENTTHREADED调用CoInitializeEx()的线程被称作是主STA。每一次用CoCreateInstance()创制的Single型组件实际上都以创办在了这么些主STA中,而不管是什么人调用了CoCreateInstance()这几个函数。全数对那么些Single组件方法的调用都必要求透过那些主STA。

  6、若STA创制STA型组件,是平昔开立,直接调用。若STA创造MTA型组件,系统为组件成立多个MTA,STA通过代理访问组件。若STA创立Both型组件,是直接成立,间接调用。若MTA创制STA型组件,系统为组件创造一个STA,MTA通过代理访谈组件。若MTA创立MTA型组件,是直接创建,直接调用。若MTA创造Both型组件,是一向创建,直接调用。可见即使顾客程序和组件都协理同样的线程方式,那么COM就同意顾客程序直接调用对象,这样将生出最棒质量。

  7、Both型组件已经很好了,无论是STA仍然MTA都可以直接创制调用它。但跨公寓的调用依然要经过代理。为了更进一竿以获得最棒质量,能够动用自由线程调解器(FTM)。注意其余门类的零部件也得以使用FTM,只是由Both使用FTM可猎取是极品效果。FTM实现了接口I马尔斯hal,当调节这三个调节接口指针的函数时,那五个函数(见5)内部调用I马尔斯hal内的有关函数,并决断若是调整产生在一个过程内的公寓之间则向来回到接口指针;如若调整发生在进度之间恐怕远程Computer间,则调用标准的调整器,并回到指向代理对象的指针。所以可知使用FTM,就算是旅社之间也不用调解接口指针了!!

  8、FTM固然好,但利用FTM的零件必得信守某个限制:使用FTM的指标不可能直接持有未有落实FTM的靶子的接口指针;使用FTM的对象不可能具有别样公寓对象代理的引用。

  9、全局接口表(GIT)。效率范围是经过内。能够把接口指针存进表中,然后在别的公寓内把其抽取,GIT自动试行公寓间的调整,所以很方便。GIT是经过IGlobalInterfaceTable访问的。通过创办CLSID为CLSID_StdGlobalInterfaceTable的目的可调用它。

 图片 1图片 2图片 3图片 4

1)COM对象为STA,Client线程伊始化为COINIT_APARTMENTTHREADED
  COM Runtime将创立STA
COM对象,Client的线程进入这一个STA并间接获得COM对象指针.

2)COM对象为STA,Client线程初叶化为COINIT_MULTITHREADED
  假使那么些COM对象未有被确立过,COM
Runtime将为那几个COM对象创立三个新的线程并在那一个新线程中树立STA COM对象,
Client线程步向的是MTA,获得的将是新的线程中那么些COM对象的被marshal的指针.如果这一个COM对象被确立过,COM
Client线程获得的将是早已建构的STA线程中COM对象的被marshal的指针.

3)COM对象为MTA,Client线程开头化为COINIT_APARTMENTTHREADED
  借使这一个COM对象没有被确立过,COM
Runtime将为那几个COM对象建设构造贰个新的线程并在那些新线程中成立MTA COM对象,
COM
Client线程步入STA,得到的将是新的线程中这些COM对象的被marshal的指针.倘若那些COM对象被确立过,Client线程得到的将是已经济建设立的MTA线程中COM对象的被marshal的指针.

4)COM对象为MTA,COM Client为COINIT_MULTITHREADED
  假诺那一个COM对象未有被确立过,COM Runtime将创立MTA COM对象.COM
Client线程步入MTA并一贯拿走COM对象的指针.如果那么些COM对象被初步化过,
Client线程步入曾经济建设立的MTA并一贯获得COM对象的指针.

5)COM对象为Any,COM Client为COINIT_APARTMENTTHREADED
  如若这几个COM对象未有被初阶化过,COM Runtime将确立STA
COM对象,Client线程步向STA,直接拿走COM对象指针.

6)COM对象为Any,COM Client为COINIT_MULTITHREADED
  固然那一个COM对象未有被伊始化过,COM Runtime将确立MTA
COM对象,Client线程步向那些MTA并一贯得到COM对象的指针.如若那一个COM对象被初始化过,Client线程步向曾经创制的MTA并平昔拿走COM对象的指针.

  1. 关于marshal
      在COM中marshal分为二种: 进度内的marshal, 同一Computer中经过间的marshal,
    以及差别计算机间的marshal. 进程内和经过间的marshal是因此Local
    RPC达成的,Computer间的marshal通过DCE RPC来落成.
    进程间和Computer间的marshal是必得的,进度内marshal是在不相同Apartment之间展开药格局调用和传递对象Interface时爆发.在同一Apartment内的调用用不着marshal.
    举个例证来讲, 三个处在MTA中的Client线程,想要调用三个地处STA中的对象时,
    COM Runtime会走进去, 对这些调用进行marshal.为何要marshal?
    因为MTA自个儿表明了前几天是四个二十八线程的条件,
    而STA中的对象不是Thread-safe的,那么对这几个不是Thread-safe的对象的调用一定要种类化(排队).COM
    Runtime为了保险不是Thread-safe的指标的调用种类化,
    必要求收获对该对象的调用, 然后打开排队. marshal正是起那个效率.
    实际上,COM
    Runtime会截获MTA的线程对STA对象的调用(通过Proxy),将以此调用通过消息传递情势传递给STA对象的stub,
    在成就调用后由stub将结果传到Proxy. 对于目的的Interface,
    也是一模二样的道理.
           对于COM+的Thread-Neutral Apartment比较独特,
    它是直接须要marshal的,
    多个方面是因为它地处不一样的经过中.别的一个要害原因是,
    COM+提供了一种类新的效应, 如Object Pooling, Object Construct String等.
    COM+必得求收获Client对COM对象的调用本事不负众望将COM对象从缓冲池中收取以及放回缓冲池等的操作.
           那么marshal是自行依旧手工业实现的吧?
    方法调用是全自动完毕的.对象的Interface的传递,日常景色下, 
    是自动完毕的,举例您通过调用CoCreateInstanceEx,CoGetClassObject等获得的指标Interface,以及经过措施调用传递的靶子Interface.不过某些情形下必需手工marshal.
    依旧形象写,比方: 比方小编有四个COM Server, 它监视三个工控装置的时域信号,
    假如非信号有丰硕,它要文告顾客端,让客商端进行报告警察方动作.为了完毕那一个效应,
    客商端和自己的COM Server通过IConnectionPoint实现事件触发机制.为了加强质量,
    COM
    Server的主线程接受顾客端通过IConnectionPoint的Advise传来的Interface指针,
    并将之放到一个Interface指针表里, 主线程运转在STA中.
    另外创设了二个周转于MTA中的线程特地用来监视时域信号,要是复信号至极,它将调用Interface指针表里全体Interface的办法来打招呼客商端.
    以后一经知道COM的编写制定的人见状本身那几个达成格局就知晓那之中须要对Interface指针实行手工业marshal.
    为什么, 顾客端通过IConnectionPoint传给我的COM
    Server主线程的Interface指针是机动举办了marshal, 可是, 由于自己的COM
    Server的非常用来监视能量信号的线程运维在和主线程差别的Apartment之中,
    对这么些线程来讲, 那一个Interface指针是从未有过通过marshal的,
    在调用是就会师世RPC_E_WRONG_THREAD错误. 要消除那个主题材料,有多个点子, 
    1) 让笔者的主线程也运维在MTA中.这种方法轻便.
    2) 手工marshal. 
         在主线程中得到顾客端的Interface指针后,
    调用Co马尔斯halInterThreadInterfaceInStream,
    获得三个IStream的指针,让后将它内置IStream的指针表里,
    监视数字信号的线程要通告顾客端时,从IStream的指针表获得IStream指针,
    然后调用CoGetInterfaceAndReleaseStream获得marshal后的顾客端Interface指针.
    这种情势有个缺欠,
    就是一旦调用CoGetInterfaceAndReleaseStream后这几个IStream指针就被放飞掉了,下二遍就取不到了.
    更加好的化解方法是利用GIT(global interface table),
    主线程将它赢得的Interface指针放到GIT,
    监摄像限信号的线程从GIT中取到的Interface指针是没有错marshal了的.
    GIT是壹个COM对象,
    有几个法子提供Interface指针的存取,使用也很简短,那儿就相当的少说了,具体请参考帮助.

 

小结精湛:

谈到COM的线程方式,实际上指的是八个地方,三个是顾客程序的线程格局,三个是组件所帮助的线程方式。客户程序的线程方式唯有二种,单线程公寓(STA)和四线程公寓(MTA)。组件所帮忙的线程格局有各类:Single(单线程)、Apartment(STA)、Free(MTA)、Both(STA+MTA)。 
 
2。
公寓只是个逻辑上的定义。贰个STA只好分包贰个线程,叁个MTA能够包括多少个线程。八个经过能够蕴含七个STA,但不得不有一个MTA。MTA中各线程能够互相的调用本公寓内实例化的机件,而没有须求实行调治。跨公寓调用组件实例必定要开展调治。(除非采取了随意线程调节器)。 
 
3。
顾客程序的线程是在调用CoInitializeEx()时间调控制客商线程的类其他。假诺以参数
COINIT_APARTMENTTHREADED调用,则会创立二个STA公寓,顾客线程包罗在这么些公寓里。借使以参数COINIT_MULTITHREADED调用,则开创贰个MTA公寓,把线程到场到这些MTA中;倘使经过内早就有了贰个MTA,则不创立新的MTA,只把线程加入到已有的MTA。注意每一种线程都必得调用CoInitializeEx()技能使用COM组件。 
 
4。
线程最珍视的是共同难点。STA是通过窗口消息队列来消除这几个题指标。当顾客线程以
COINIT_APARTMENTTHREADED调用CoInitializeEx()时,将为会该STA创设一个有着
奥莱MainThreadWndClass窗口类的含有窗口。全数对在这些公寓中国建工业总会公司立的COM对象方法的调用都将都停放这么些蕴藏窗口的新闻队列中。所以每一个与STA相关联的线程必需用
GetMessage、DispatchMessage或左近方法来平均分摊窗口新闻。MTA内各线程可并行调用同一个组件对象的实例,进而不保障安全性,所以达成联机访谈的义务就落在了组件身上。注意,STA的联合是公寓级的,就是说对饭店内区别组件的探访都要放置同三个新闻队列中,对三个实例的法子调用会影响对别的实例的调用,所以并发程度好低。 
 
5。
在分化公寓间传递接口指针必须要由此调治。那第一如故为着共同对组件的调用。通过Co马尔斯halInterThreadInterfaceInStream和CoGetInterfaceAndReleaseStream达成。很轻松。 
 
6。
Single型组件很奇特,它只好在一个单纯的线程中实践。首先要说明的是一个进程中率先个以COINIT_APARTMENTTHREADED调用CoInitializeEx()的线程被称作是主STA。每一次用CoCreateInstance()成立的Single型组件实际上都以创办在了那么些主STA中,而不管是哪个人调用了CoCreateInstance()这几个函数。全数对这几个Single组件方法的调用都无法不要经过那个主STA。 
 
7。 若STA创设STA型组件,是一贯创建,直接调用。
若STA创设MTA型组件,系统为组件创造贰个MTA,STA通过代办访谈组件。
若STA创设Both型组件,是直接开立,直接调用。
若MTA创建STA型组件,系统为组件成立多个STA,MTA通过代办访谈组件。
若MTA成立MTA型组件,是平素开立,直接调用。
若MTA创立Both型组件,是直接创设,直接调用。
可知如若客商程序和组件都帮忙一样的线程格局,那么COM就同意顾客程序直接调用对象,那样将发出最好性能。
 
8。
Both型组件已经很好了,无论是STA照旧MTA都能够直接开立调用它。但跨公寓的调用依然要经过代理。为了更上一层楼以得到最棒品质,可以动用自由线程调治器(FTM)。注意别的门类的零部件也得以使用FTM,只是由Both使用FTM可收获是极品效果。FTM实现了接口I马尔斯hal,当调治那三个调整接口指针的函数时,那多少个函数(见5)内部调用I马尔斯hal内的有关函数,并判别如若调节发生在一个进程内的酒店之间则一向回到接口指针;
假定调节产生在进程之间或许远程Computer间,则调用规范的调节器,并回到指向代理对象的指针。所以可知使用FTM,固然是旅馆之间也不用调整接口指针了!! 
 
9。
FTM即使好,但使用FTM的零件必需信守有些限制:使用FTM的目的不能直接持有未有落到实处FTM的靶子的接口指针;使用FTM的对象无法具备别样公寓对象代理的援用。

 
10。
全局接口表(GIT)。成效范围是进度内。能够把接口指针存进表中,然后在其他公寓内把其抽出,GIT自动施行公寓间的调解,所以很有利。GIT是通过IGlobalInterfaceTable访谈的。通过创办CLSID为CLSID_StdGlobalInterfaceTable的目的可调用它。

相关文章