1. 程式人生 > >.NET的兩種執行緒模型:STA和MTA

.NET的兩種執行緒模型:STA和MTA

參考資料:
http://www.yesky.com/20010207/158097.shtml
http://www.ftponline.com/china/XmlFile.aspx?ID=242
http://research.microsoft.com/~chadv/java_com2.htm
http://blogs.msdn.com/jfoscoding/archive/2005/04/07/406341.aspx


.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執行緒為需要維護內部隱藏的視窗類和訊息佇列而增加的。

下面是實現方法:

[csharp] view plaincopyprint?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading;  
  6. using Microsoft.Office.Interop.Word;  
  7. using System.Windows.Forms;  
  8. using System.Drawing;  
  9. namespace ConsoleApplication1  
  10. {  
  11. class STAANDMTA  
  12.     {  
  13. static
    void Main()  
  14.         {  
  15.             Thread t = new Thread(new ThreadStart(CopyImages));  
  16.             t.ApartmentState = ApartmentState.STA;  
  17.             t.Start();  
  18.         }  
  19. privatestaticvoid CopyImages()  
  20.         {  
  21.             Microsoft.Office.Interop.Word.Application app = null;  
  22.             Microsoft.Office.Interop.Word.Document doc = null;  
  23. object missing = System.Reflection.Missing.Value;   
  24.             app = new Microsoft.Office.Interop.Word.Application();  
  25. try
  26.             {  
  27. object fileName = @"E:\A.doc";  
  28.                 doc = app.Documents.Open(ref fileName, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,  
  29. ref missing, ref missing, ref missing, ref missing, ref missing, ref missing);  
  30. int count = doc.InlineShapes.Count;  
  31. for (int i = 1; i <= count; i++)  
  32.                 {  
  33.                     doc.InlineShapes[i].Range.Copy();//複製到貼上板
  34. if (System.Windows.Forms.Clipboard.GetDataObject() != null)  
  35.                     {  
  36.                         IDataObject data = Clipboard.GetDataObject();  
  37. if (data.GetDataPresent(DataFormats.Bitmap))  
  38.                         {  
  39.                             Image image = (Image)data.GetData(DataFormats.Bitmap, true);  
  40.                             image.Save("E:\\" + i.ToString() + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);  
  41.                         }  
  42. else
  43.                         {  
  44. //lst_Items.Items.Add(doc.Name + ";無正確圖片資料");
  45.                         }  
  46.                     }  
  47. else
  48.                     {  
  49. //lst_Items.Items.Add(doc.Name + ";貼上板為空");
  50.                     }  
  51.                 }  
  52.             }  
  53. catch (Exception ex)  
  54.             {  
  55. //lst_Items.Items.Add(doc.Name + "發生錯誤;" + ex.Message);
  56.             }  
  57. finally
  58.             {  
  59. if (doc != null)  
  60.                     doc.Close(ref missing, ref missing, ref missing);  
  61. if (app != null)  
  62.                     app.Quit(ref missing, ref missing, ref missing);  
  63.             }  
  64.         }  
  65.     }  
  66. }