STA和MTA線程模式的區別


文章來源: 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            }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM