首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 組件執行緒模型與 COM Interop 線程封送深度解析

     編輯:關於.NET

背景:從 COM 單元模型到 .NET Context

經典 COM 使用「單元」 管理執行緒親和性:STA(單執行緒單元)、MTA(多執行緒單元)。.NET 引入 AppDomain(輕量級進程)與 Context(內含同步、交易、JIT 啟動等需求的執行環境)。物件依其屬性(如 [Synchronization])被置於特定 Context,執行期自動處理跨 Context 呼叫的封送。

.NET 組件的執行緒親和性分類

跨 Context 存取方案

Context-Agile 物件:不繫結特定 Context,可在任何執行緒直接執行,無封送開銷。預設所有非 ContextBoundObject 衍生類別皆為 Agile。

非受控 COM 用戶端呼叫 .NET 組件:ThreadingModel="Both" 行為

經 RegAsm.exe 註冊後,登錄 InprocServer32 下 ThreadingModel 為 Both。表示:

  • 物件進入呼叫者單元(STA 或 MTA)。
  • 支援自由執行緒封送器 (FTM),可直接傳遞介面指標無 Proxy。

.NET Context-Agile 組件行為等同 COM "Both" + FTM:永遠在呼叫者執行緒上執行,無執行緒切換。

實驗驗證:C++ 用戶端跨單元呼叫 .NET 組件

.NET 組件代碼

using System;
using System.Runtime.InteropServices;

[ComVisible(true)]
public interface IHelloDotNet
{
    string GetThreadId();
}

[ClassInterface(ClassInterfaceType.None)]
public class HelloDotNet : IHelloDotNet
{
    public string GetThreadId() => AppDomain.GetCurrentThreadId().ToString();
}

編譯註冊:

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

C++ 主控台應用程式

#import "mscorlib.tlb"
#import "HelloDotNet.tlb" no_namespace

IHelloDotNetPtr spHelloNET;
IStream* g_pStream1 = NULL;
IStream* g_pStream2 = NULL;

long WINAPI MySTAThreadFunction(long lParam);
long WINAPI MyMTAThreadFunction(long lParam);

int main()
{
    CoInitialize(NULL); // 主執行緒進入 STA
    cout << "Main STA Thread ID: " << GetCurrentThreadId() << endl;

    spHelloNET.CreateInstance(__uuidof(HelloDotNet));
    cout << "From .NET (Main STA): " << spHelloNET->GetThreadId() << endl;

    // 封送介面至兩個背景執行緒
    CoMarshalInterThreadInterfaceInStream(__uuidof(IHelloDotNet), spHelloNET, &g_pStream1);
    CoMarshalInterThreadInterfaceInStream(__uuidof(IHelloDotNet), spHelloNET, &g_pStream2);

    HANDLE hSTA = CreateThread(NULL, 0, MySTAThreadFunction, NULL, 0, NULL);
    HANDLE hMTA = CreateThread(NULL, 0, MyMTAThreadFunction, NULL, 0, NULL);

    WaitForSingleObject(hSTA, INFINITE);
    WaitForSingleObject(hMTA, INFINITE);
    return 0;
}

long WINAPI MySTAThreadFunction(long lParam)
{
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); // 進入 STA
    cout << "STA Worker Direct: " << spHelloNET->GetThreadId() << endl;

    IHelloDotNetPtr spHello;
    CoGetInterfaceAndReleaseStream(g_pStream1, __uuidof(IHelloDotNet), (void**)&spHello);
    cout << "STA Worker Marshaled: " << spHello->GetThreadId() << endl;
    return 0;
}

long WINAPI MyMTAThreadFunction(long lParam)
{
    CoInitializeEx(NULL, COINIT_MULTITHREADED); // 進入 MTA
    cout << "MTA Worker Direct: " << spHelloNET->GetThreadId() << endl;

    IHelloDotNetPtr spHello;
    CoGetInterfaceAndReleaseStream(g_pStream2, __uuidof(IHelloDotNet), (void**)&spHello);
    cout << "MTA Worker Marshaled: " << spHello->GetThreadId() << endl;
    return 0;
}

執行結果分析

Main STA Thread ID: 2220
From .NET (Main STA): 2220
STA Worker Thread ID: 2292
MTA Worker Thread ID: 2296
From .NET (STA Worker Direct): 2292
From .NET (STA Worker Marshaled): 2292
From .NET (MTA Worker Direct): 2296
From .NET (MTA Worker Marshaled): 2296

結論:所有呼叫均在呼叫者執行緒上執行,無執行緒切換。直接呼叫與封送呼叫結果相同,證明 .NET 組件提供中立執行緒介面指標,行為等同 COM "Both" + FTM。

總結:COM 與 .NET 共存策略

  1. 既有大量 COM 代碼:善用 Interop 互操作,漸進式遷移。
  2. 新業務邏輯開發:優先使用 C#/VB.NET 撰寫 Managed 組件,避免跨邊界效能損耗。
  3. 工具鏈成熟:RegAsm、TlbImp、ComVisible、ClassInterface 等屬性提供精細控制。

COM 與 .NET 的融合並非對立,而是互補。理解雙方執行緒模型映射關係,是構建穩健混合系統的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved