程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET組件中的線程輔助

.NET組件中的線程輔助

編輯:關於.NET

在Understanding COM Threading Models and Apartments from a .NET Application's perspective 這一章,你知道在創建經典COM組件之前,.NET 應用程序是怎樣宣告調用線程單元輔助。現在看一下等式的另一方面。尤其是當從非托管 COM感知應用程序創建了.NET組件時,.NET組件的線程輔助被文本定義,而對象就處在這個文本中。本質上來說,一個Context就是AppDomain(輕型過程)擁有的環境 ,而對象就是在AppDomain 中創建的。每一個文本都依次擁有對象,這些對象享用公共的使用要求,例如:線程輔助、對象池化、交易、JIT 激活、同步等。當依靠屬性的執行時要求時,而且對象也要求中斷服務時,就會創建這些文本。如果這裡有一個文本,此嗯本與使用規則相匹配,然後執行時就會在那個文本中提供調節。如果它沒有找到一個相匹配的文本,就會為對象創建一個新文本。

前面說到,每一個AppDomain都有一個默認文本。默認文本.依次擁有Context Agnostic (Context Agile)對象。這些對象沒有綁定到任何文本。Context Agile 對象不要求任何屬性,特殊使用規則以及中斷服務。讓我們看一下下面的表格,此表格總結了基於它們自己文本敏捷度的跨文本訪問方案中.NET 組件是怎樣執行的

圖1

被非托管COM感知客戶端訪問時的線程中立行為

當一個程序集經過REGASM.EXE,想為COM 感知客戶端創建正確的注冊途徑時,.NET組件是怎樣把它的線程模式轉到COM的。

InprocServer32下面的ThreadingModel鑰有一個'Both'值。在Classic COM中,把它們的ThreadingModel作為'Both'的對象,期望移到它們的呼叫者單元,此呼叫者單元可以是STA或者MTA。除此以外,'Both'線程對象也集合自由線程封送拆收器,給被它們封送拆收事物提供單元,也集合直接接口指針引用,以此反對代理。Context Agile .NET 組件(不是從ContextBound對象擴展而來的)與中立線程COM對象相似,此中立線程COM對象集合自由線程封送拆收器。當我們通過非托管客戶端中的COM 單元將接口引用傳輸到.NET組件時,.NET組件是怎樣運行的。讓我們看一下這個簡單的C#類型:將向非托管 COM客戶端展示這個C#類型:

using System;
using System.Runtime.InteropServices;
public interface IHelloDotNet {
String GetThreadID();
}/* end interface IHelloDotNet */
[ClassInterface(ClassInterfaceType.None)]
public class HelloDotNet : IHelloDotNet
{
public HelloDotNet() {
}
public String GetThreadID() {
return AppDomain.GetCurrentThreadId().ToString();
}
}/* end class HelloDotNet */

上面的類型執行來自於IHelloDotNet接口的GetThreadID方法。這個方法返回當前線程的ID,此當前線程正將AppDomain運行到這個對象下載的事物裡。為了把上面的類型建成一個程序集,為COM創建正確注冊途徑,從命令行中執行下列命令。

csc /target:library /out:HelloDotNet.dll HelloDotNet.cs

regasm HelloDotNet.dll /tlb:HelloDotNet.tlb

現在繼續使用來自於COM感知客戶端的.NET組件。我們將使用一個C++ 控制台應用程序,此C++ 控制台應用程序將在它的主線程 (一個STA)中創建.NET組件,然後通過生成其它兩個背景工作執行緒,把它傳送到另外兩個單元(一個STA單元和一個MTA單元)。讓我們看一下:當使用顯式線程間封送拆收調用(此調用使用CoMarshalInterface/CoUnmarshalInterface API家族)時,什麼時候被封送拆收的引用將在單元傳輸。看一下下面的代碼(為了簡潔,省略了代碼中的錯誤檢查)。

Collapse
.......
#import "mscorlib.tlb"
// 導入 .NET 組件
#import "HelloDotNet.tlb" no_namespace
// 線程函數
long WINAPI MySTAThreadFunction(long lParam);
long WINAPI MyMTAThreadFunction(long lParam);
IHelloDotNetPtr spHelloNET = NULL;
IStream* g_pStream1 = NULL;
IStream* g_pStream2 = NULL;
int main(int argc, char* argv[])
{
.......
::CoInitialize(NULL);
cout << "The Thread ID of the primary STA thread is : "
<< ::GetCurrentThreadId() << endl;
hr = spHelloNET.CreateInstance(__uuidof(HelloDotNet));
cout << "From .NET when called from the primary STA Thread : "
<< spHelloNET->GetThreadID() << endl;
.......
hr = CoMarshalInterThreadInterfaceInStream(_uuidof(IHelloDotNet),
spHelloNET,
&g_pStream1);
hr = CoMarshalInterThreadInterfaceInStream(_uuidof(IHelloDotNet),
spHelloNET,
&g_pStream2);
hThreadSTA = CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)MySTAThreadFunction,
NULL,0 ,&dwThreadIDSTA);
cout << "The Thread ID of the STA based Worker thread is : "
<< dwThreadIDSTA << endl;
hThreadMTA = CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)MyMTAThreadFunction,
NULL,0,&dwThreadIDMTA);
cout << "The Thread ID of the MTA based Worker thread is : "
<< dwThreadIDMTA << endl;
  
::WaitForSingleobject(hThreadSTA,INFINITE);
::WaitForSingleobject (hThreadMTA,INFINITE);
return 0;
}
long WINAPI MySTAThreadFunction(long lParam)
{
::CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);
cout << "From .NET when called from the STA Worker Thread (Direct Access) : "
<< spHelloNET->GetThreadID() << endl;
IHelloDotNetPtr spHello = NULL;
HRESULT hr = CoGetInterfaceAndReleaseStream(g_pStream1,
__uuidof(IHelloDotNet),
(void **)&spHello);
if(S_OK == hr)
{
cout << "From .NET when called from the STA Worker Thread (Marshaled) : "
<< spHello->GetThreadID() << endl;
}
return 0;
}

long WINAPI MyMTAThreadFunction(long lParam)
{
// 讓線程進入MTA
::CoInitializeEx(NULL,COINIT_MULTITHREADED);
cout << "From .NET when called from the MTA Worker Thread (Direct Access) : "
<< spHelloNET->GetThreadID() << endl;
IHelloDotNetPtr spHello = NULL;
HRESULT hr = CoGetInterfaceAndReleaseStream(g_pStream2,
__uuidof(IHelloDotNet),
(void **)&spHello);
if(S_OK == hr)
{
cout << "From .NET when called from the MTA Worker Thread (Marshaled) : "
<< spHello->GetThreadID() << endl;
}
// 從線程退出
return 0;
}/* 結束MyMTAThreadFunction */

當運行控制台應用程序時,這是得到的輸出。

The Thread ID of the primary STA thread is : 2220
From .NET when called from the primary STA Thread : 2220
The Thread ID of the STA based Worker thread is : 2292
The Thread ID of the MTA based Worker thread is : 2296
From .NET when called from the STA Worker Thread (Direct Access) : 2292
From .NET when called from the STA Worker Thread (Marshalled) : 2292
From .NET when called from the MTA Worker Thread (Direct Access) : 2296
From .NET when called from the MTA Worker Thread (Marshalled) : 2296

注意:對於所有調用,在客戶端中的線程呼叫之間,不會產生線程交換,在.NET組件中,也不會產生線程引發實際方法。還句話說,.NET組件是一個Context agile,總是在呼叫者線程裡執行任務。從上面的代碼片段中觀察到:對象引用(例如:CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream)的封送拆收效果與在單元間傳遞直接對象引用的效果是一樣的。最後,接受單元得到一個中立單元接口指針,此中立單元l接口指針能用來調用.NET組件。.NET組件展示Both 線程Classic COM 組件回憶的所有行為,而Both線程Classic COM 組件則集合the自由線程封送拆收器。

結論:.NET世界中COM組件的地位

在這系列文章的第一部分:我們可以怎樣把Classic COM組件展示到在公共語言運行庫 (通用語言執行層)范圍內執行的.NET應用程序。通過使用前期綁定和晚期綁定,以及檢查執行時類型和創建動態類型發現的方法,COM interop怎樣無縫地允許你來調用你的COM組件。我們已經理解了在.NET 中,委托是怎樣工作的,以及它們在.NET時間處理模式中所扮演的角色,以及作為適配器,COM Interop是怎樣把classic COM中的連接點事件處理模式連接到.NET中的基於委托的事件處理模式。我們討論了怎樣把COM集合展示到.NET應用程序中,以及怎樣使用C#'s foreach句法簡單地通過集合元素來迭代。然後,我們研究了IDL文件中的方向屬性是怎樣被映射到C#中相應的方向參數類型。我們也學習了:通過使用繼承和內含包容,Classic COM組件可以利用的來自於.NET 應用程序的重用選項。最後知道當調用COM組件時,托管線程是怎樣宣告它們的單元輔助。

在文章的後半部分,我們嘗試探索來自於.NET時代以前的COM感知客戶端是怎樣像classic COM組件一樣使用.NET 組件。從程序編輯方面來看,COM可調用的包裝 以及通用語言執行層怎樣無縫地推動產生此程序。我們簡單地探索了使用屬性將元數據發送到.NET類型的可能性,因此可以根據你的要求來修改被生成的類型庫。我們也學習了兩個世界中異常處理機制是怎樣互相聯系的。我們還討論了怎樣從處在非托管事件接受裡的.NET 組件中接受異步事件通知。然後把注意力轉向可以利用的安裝選擇,以及怎樣把.NET 組件安裝成共享程序集.最後討論了.NET 組件裡的中立線程行為,知道Context-agile .NET 組件如何與集合自由線程封送拆收器 (FTM)的線程Classic COM 'Both'組件相似。

作為COM開發者可能想知道繼續編寫COM組件,或者通過鍵入所有組件和商業邏輯代碼(此商業邏輯代碼是通過使用其中一種語言,例如:C#, VB.NET或者任何其它你所喜歡的生成通用語言執行層托管代碼的語言包裝成托管 組件),並且直接轉移到.NET世界是非常有意義的。依我看來,如果有大量的COM代碼,而不能一夜之間把這些COM代碼轉化成托管代碼,綜合利用interop的能力,重用來自於.NET應用程序的COM 組件。但是如果開始從空開始編寫新商業邏輯代碼,然後,最好的方法就是使用其中一種生成通用語言執行層托管代碼的語言,將代碼包裝成托管組件。通過這種方法,在托管和非托管邊界之間傳輸時可以免受性能終結之苦。從程序設計方面來看,不管.NET應用程序正在訪問一個Classic COM組件還是訪問一個托管組件,由.NET結構提供的工具以及由運行庫提供的COM interop機制都能使它無縫。因此本質上,COM和勇敢的新用戶.NET世界結合將非常幸福,我們所知道的、喜歡的COM仍然繼續我們生活的精髓。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved