文章來源: http://blog.csdn.net/fragno/article/details/7982044
STA: Single-Thread Apartment, 中文叫單線程套間。就是在COM庫初始化的時候創建一個內存結構,然后讓它和調用CoInitialize的線程相關聯。這個內存結構針對每個線程都會有一個。支持STA的COM對象只能在創建它的線程里被使用,其它線程如果再創建它就會失敗。
MTA: Mutil-Thread Apartment,中文叫多線程套間。COM庫在進程中創建一個內存結構,這個內存結構在整個進程中只能有一個,然后讓它和調用CoInitializeEx的線程相關聯。支持MTA的COM對象可以在任意線程里被使用。多有針對它的調用都會被封裝成為消息。
其實STA和MTA是COM規定的一套線程模型,用於保障多線程情況下你的組件代碼的同步。比如說有一個COM對象它內部有一個靜態變量 gHello,那么這個對象無論生成多少實例對於gHello在內存中只能有一份,那么如果有兩個不同的實例在兩個線程里面同時去讀寫它,就有可能出錯,所以就要就要有種機制進行同步保護,STA或者MTA就是這種機制。
進程相對於一個小城鎮,線程相當於這個城鎮里的居民,STA(單線程套間)相當於居民房,是私有的,MTA(多線程套間)相當於旅館,是公用的,Com對象相當於居民房或旅館里的物品.接下去就好理解了,一個小城鎮(進程)里可以有很多很多的(居民)線程,這個城鎮(進程)只有一間旅館(MTA),但可以有很多很多的居民房(STA).只有居民(線程)進入了房間(居民房或旅館,STA或MTA)以后才能使用該房間里的物品(COM對象),居民房(STA)里的物品(COM對象)只能供這間房子的主人(創建該STA的線程)使用,其它居民(線程)不能訪問.同樣,只有入住到旅館(MTA)里的居民(線程,可以有多個)才可以訪問到旅館(MTA)里的物品(com對象),但因為是公用的,所以要合理的分配(同步)才能不會產生混亂.
.NET支持兩種線程模型:STA和MTA。
STA(single threaded apartments)。apartment只是一個邏輯上的概念,它可以包含一個或多個線程。一個AppDomain可以包括一個或多個apartment。STA是指該apartment中只能包含一個thread。
MTA(multi threaded apartments)。指該apartment中可以包含多個thread。
STA and MTA 之間最大的區別就是MTA 可以在同一個apartment 中使用所有的共享資源並發執行多個線程。 而多個STA雖然可以共享數據,但是不能並發執行線程,存在性能問題。
線程的創建:
當創建一個新的STA線程時,CLR會在該AppDomain中創建一個apartment和thread(從屬於該apartment)。如果是創建MTA線程,則會CLR會檢查該AppDomain是否存在一個存放MTA的apartment,如果存在僅創建該線程到該MTA中,否則就創建一個MTA和thread(從屬於該apartment)。
我們可以設置線程的屬性。例如 t.ApartmentState = ApartmentState.STA;
線程的使用區別:
我們應該僅僅在訪問STA-based 的COM組件時才使用STA線程模式。可以在注冊表的HKEY_CLASSES_ROOT\CLSID\{Class ID of the COM component} \InProcServer32 下查看到該COM的線程模式。如果該值是Apartment,則說明該COM只能以STA模式運行。其他的值有Free(MTA),Both(STA+MTA),Single(只能在一個單一的線程中執行)。
其他情況下,我們應該使用MTA的線程,雖然需要我們費心線程間資源的同步問題。
示例:
我現在想在一個windows form的程序中實現從某個word文檔復制圖片並保存的方案。
具體是:打開word文檔,將圖片信息復制到粘貼板中,然后從粘貼板中取得圖片信息,再保存到本地目錄中。
說明:(本來是放在代碼下面的,無奈POST之后就被代碼擋住不顯示了)
如果在某個按鈕的事件中,直接調用該方法,那么界面將變得沒有響應。所以我們需要考慮使用多線程來解決這個問題。Thread t = new Thread(new TheardStart(CopyImages); t.Start();
如果是這樣,則程序會發生錯誤.。要么顯示出現異常,要么沒異常但是Clipboard為空,取不到任何數據!為什么呢?
因為Word.Application 是Automation並且STA-Based,不能在沒有指定ThreadApartment的線程中被調用。所以導致了各種錯誤,所以需要在t.Start();前面加上t.Apartment = ApartmentState.STA;這樣就完全正常了。
對於MTA的多線程我們就見的比較多了,不再舉例了。
另外一點不明白,我監視任務管理器發現,我在執行Thread t = new Thread(new TheardStart(CopyImages);t.Apartment = ApartmentState.STA; t.Start();之后該程序的進程中線程數從3個增加到6個,如果創建的是MTA的線程則只增加1。我的理解是STA線程為需要維護內部隱藏的窗口類和消息隊列而增加的。
下面是實現方法:
1private void CopyImages()
2 {
3 Word.Application app = null;
4 Word.Document doc = null;
5
6 app = new ApplicationClass();
7
8 try
9 {
10 object fileName = @"E:\A.doc";
11 doc = app.Documents.Open(ref fileName,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,
12 ref missing,ref missing,ref missing,ref missing,ref missing,ref missing);
13
14 int count = doc.InlineShapes.Count;
15 for(int i=1;i<=count;i++)
16 {
17 doc.InlineShapes[i].Range.Copy();
18
19 if (Clipboard.GetDataObject() != null)
20 {
21 IDataObject data = Clipboard.GetDataObject();
22
23 if (data.GetDataPresent(DataFormats.Bitmap))
24 {
25 Image image = (Image)data.GetData(DataFormats.Bitmap,true);
26 image.Save("E:\\" + i.ToString() + ".jpg",System.Drawing.Imaging.ImageFormat.Jpeg);
27 }
28 else
29 {
30 lst_Items.Items.Add(doc.Name + ";無正確圖片數據");
31 }
32 }
33 else
34 {
35 lst_Items.Items.Add(doc.Name + ";粘貼板為空");
36 }
37 }
38
39 }
40 catch(Exception ex)
41 {
42 lst_Items.Items.Add(doc.Name + "發生錯誤;" + ex.Message);
43 }
44 finally
45 {
46 if (doc != null)
47 doc.Close(ref missing,ref missing,ref missing);
48 if (app != null)
49 app.Quit(ref missing,ref missing,ref missing);
50 }