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

C# 使用 IOCP 完成端口傳遞物件參考完整範例(三)

     編輯:關於C語言

前言

承接前兩篇,本文解決 IOCP 完成端口在 managed/unmanaged 邊界傳遞物件參考的核心難題:利用 GCHandle 取得物件指標,配合 PostQueuedCompletionStatus / GetQueuedCompletionStatus 實現型別安全的資料傳遞。

關鍵 API 宣告

[DllImport("Kernel32")]
private static extern bool PostQueuedCompletionStatus(
    UInt32 completionPort,
    int numberOfBytesTransferred,
    IntPtr completionKey,
    IntPtr overlapped);

[DllImport("Kernel32")]
private static extern bool GetQueuedCompletionStatus(
    UInt32 completionPort,
    ref int numberOfBytes,
    ref IntPtr completionKey,
    ref IntPtr overlapped,
    UInt32 milliseconds);

GCHandle 物件釘選與傳遞

int key = 0;
object data = new object();           // 任意 managed 物件
GCHandle gcData = GCHandle.Alloc(data);   // 釘選,防止 GC 移動
GCHandle gcKey = GCHandle.Alloc(key);

// 發送至完成端口
PostQueuedCompletionStatus(portHandle, 4, (IntPtr)gcKey, (IntPtr)gcData);

工作執行緒接收與還原

uint bytes;
IntPtr recvKey, recvOverlapped;
GetQueuedCompletionStatus(port, out bytes, out recvKey, out recvOverlapped, INFINITE);

GCHandle gch = GCHandle.FromIntPtr(recvOverlapped);
object originalData = gch.Target;       // 取回原始物件
gch.Free();                             // 釋放 GCHandle,讓 GC 回收

GCHandleType 選擇指南

類型行為適用場景
Normal防止 GC 回收,允許移動物件Managed↔Unmanaged 雙向傳遞,不影響 GC 效能
Pinned防止回收且固定記憶體位址傳遞 byte[] 緩衝區給 Unmanaged 代碼填充
Weak/WeakTrackResurrection弱參考,不阻止 GC快取、觀察者模式,非本文重點

完整範例程式碼

using System;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Win32.SafeHandles;

[StructLayout(LayoutKind.Sequential)]
class PerIoData { public string Data; }

public class IOCPApiTest
{
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern SafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey, uint NumberOfConcurrentThreads);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool GetQueuedCompletionStatus(SafeFileHandle CompletionPort, out uint lpNumberOfBytesTransferred, out IntPtr lpCompletionKey, out IntPtr lpOverlapped, uint dwMilliseconds);

    [DllImport("Kernel32", CharSet = CharSet.Auto)]
    private static extern bool PostQueuedCompletionStatus(SafeFileHandle CompletionPort, uint dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped);

    public static unsafe void TestIOCPApi()
    {
        var port = CreateIoCompletionPort(new IntPtr(-1), IntPtr.Zero, IntPtr.Zero, 1);
        if (port.IsInvalid)
            Console.WriteLine($"建立失敗:{Marshal.GetLastWin32Error()}");

        var worker = new Thread(ThreadProc);
        worker.Start(port);

        // 發送第一筆資料
        var data1 = new PerIoData { Data = "Hi, I'm worker." };
        var h1 = GCHandle.Alloc(data1);
        PostQueuedCompletionStatus(port, (uint)sizeof(IntPtr), IntPtr.Zero, (IntPtr)h1);

        // 發送結束訊號
        var data2 = new PerIoData { Data = "EXIT" };
        var h2 = GCHandle.Alloc(data2);
        PostQueuedCompletionStatus(port, 4, IntPtr.Zero, (IntPtr)h2);

        Console.WriteLine("主執行緒結束");
        Console.ReadKey();
    }

    static void ThreadProc(object portObj)
    {
        var port = (SafeFileHandle)portObj;
        while (true)
        {
            uint bytes; IntPtr key, overlapped;
            Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] 等待資料...");
            GetQueuedCompletionStatus(port, out bytes, out key, out overlapped, 0xFFFFFFFF);

            if (bytes == 0) continue;

            var gch = GCHandle.FromIntPtr(overlapped);
            var data = (PerIoData)gch.Target;
            Console.WriteLine($"收到:{data.Data}");
            gch.Free();

            if (data.Data == "EXIT")
            {
                Console.WriteLine("收到結束指令,退出。");
                port.Dispose();
                break;
            }
        }
    }
}

結語

GCHandle 是跨越 Managed/Unmanaged 邊界傳遞物件的關鍵。正確選擇 GCHandleType、及時呼叫 Free(),才能避免記憶體洩漏與存取違規。本範例展示最小可行的 IOCP 物件傳遞模型,可擴充為高效能非同步伺服器核心。

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