多執行緒呼叫系統COM元件的體會(CoInitialize)
多執行緒呼叫COM元件的體會(CoInitialize) 呼叫任何COM元件之前,你必須首先初始化COM套件環境,即呼叫CoInitialize或CoInitializeEx。COM套件環境線上程的生存週期內有效,執行緒退出前需要呼叫CoUninitialize釋放COM套件。
所謂COM套件,實際上是微軟為了方便大家理解而起的一個名字,不過個人認為改名詞很難理解。COM套件只指COM元件執行時的環境,其中包括COM元件的資料、變數、執行緒排程方式。
COM套件分為兩種模式,單執行緒套件(STA)和多執行緒套件(MTA)。不要單從字面上理解,例如:STA並非只能用於單執行緒的程式,多執行緒程式依然可以使用。下面列出兩種套件模式的區別。
套件型別
說明
效能 相容性
常見錯誤 STA 單執行緒套間,一個程序內所有COM元件都執行在主STA中,主STA就是第一個呼叫CoInitialize函式的執行緒。
也就是說,即使你擁有多執行緒程式,但在不通執行緒同時操作COM元件的時候,COM元件會通過Windows訊息、Event同步物件之類的機制把呼叫轉換到主STA執行,而主STA通常對應應用程式的主執行緒。這樣對STA套件內的任何COM元件操作,實際上是單執行緒操作,COM元件不必關心執行緒同步的細節,因為根本沒有必要進行執行緒同步。
低
如果一個COM元件是MTA的,可以安全的執行與STA套件中。
由於STA套件所有的COM元件程式碼都運行於主STA(第一個呼叫CoInitialize函式的執行緒),如果你的主執行緒沒有呼叫CoInitialize,那麼第一個呼叫CoInitialize的工作執行緒就會成為主STA,而工作執行緒隨時可能中止,這種情況下,一旦工作執行緒中止主STA也就不復存在了,因此你需要在主執行緒中呼叫CoInitialize初始化主STA,即使主執行緒不使用任何COM元件。
MTA 多執行緒套間,所有COM元件都執行在本執行緒的MTA套件中。
這是就會出現多個執行緒同時執行某個COM呼叫,COM元件的開發者必須預料並處理這種併發訪問帶來的記憶體競爭讀寫混亂。COM元件開發者通常會應用臨界區、互斥量、訊號燈之類的常規執行緒同步方法。而呼叫者,不需要擔心COM元件是否會因為多執行緒掛掉。
高
如果一個COM元件是STA的,被錯誤的執行與MTA套件會引發各種奇怪的錯誤。
把STA的COM元件運行於MTA套件中會引發錯誤。
這就會引出一個問題,到底我該使用STA還是MTA呢,答案很簡單問該COM元件的開發者,或者看他的說明文件,他們會告訴你。
STA套件的初始化方式(兩種方式等效):
1,CoInitialize(nil);
2,CoInitializeEx(nil,COINIT_APARTMENTTHREADED);
MTA套間的初始化方式:
1,CoInitializeEx(nil,COINIT_MULTITHREADED);
附上一個多執行緒中,工作執行緒使用STA套件,但是主執行緒沒有初始化主STA,引發的怪異錯誤的反面教材程式碼。
program STATest; {$APPTYPE CONSOLE}
uses Windows, Variants, ComObj, Classes, SysUtils, ActiveX;
type TScriptThread=class(TThread) public procedure Execute; override; end; { TScriptThread }
var ThreadTotal:Integer; ThreadCount:Integer;
procedure TScriptThread.Execute; var FVBScriptEngine:Variant; begin try
//初始化工作執行緒的STA
CoInitialize(nil);
try
FVBScriptEngine:=CreateOleObject('ScriptControl');
FVBScriptEngine.AllowUI:=False;
FVBScriptEngine.Timeout:=500;
FVBScriptEngine.Language:='VBScript';
finally
InterlockedDecrement(ThreadCount);
CoUninitialize;
end;
except on E:Exception do Writeln(E.Message); end; end;
begin
//主執行緒部分
//初始化主STA,刪掉下面一行的註釋即可讓程式穩定執行。
//CoInitialize(nil);
ThreadCount:=0; ThreadTotal:=0; try while True do begin
//保持工作執行緒始終不超過100
while ThreadCount>=100 do;
Inc(ThreadTotal);
With TScriptThread.Create(True) do begin
InterlockedIncrement(ThreadCount);
FreeOnTerminate:=True;
Start;
end;
Writeln(ThreadTotal);
end;
except on E:Exception do Writeln(E.Message); end; Readln(Input); end. 好文要頂 關注我 收藏該文