本文為系列第二篇,深入剖析 GetQueuedCompletionStatus 參數意義、GCHandle 運作機制,並提供完整的執行緒池與完成端口互動範例程式碼。
[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);
PostQueuedCompletionStatus 發送的自訂訊息,此值為 dwNumberOfBytesTransferred 參數值。GCHandle)。0xFFFFFFFF 表無限等待。GCHandle 結構。Normal 允許 GC 移動物件,Pinned 固定記憶體位址。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()}");
return;
}
var worker = new Thread(ThreadProc);
worker.Start(port);
// 發送資料 1
var d1 = new PerIoData { Data = "Hello from main thread" };
var h1 = GCHandle.Alloc(d1);
PostQueuedCompletionStatus(port, (uint)sizeof(IntPtr), IntPtr.Zero, (IntPtr)h1);
// 發送資料 2(結束訊號)
var d2 = new PerIoData { Data = "SHUTDOWN" };
var h2 = GCHandle.Alloc(d2);
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($"[Worker {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($"[Worker] 收到:{data.Data}");
gch.Free();
if (data.Data == "SHUTDOWN")
{
Console.WriteLine("[Worker] 關閉中...");
port.Dispose();
break;
}
}
}
public static int Main() { TestIOCPApi(); return 0; }
}
IntPtr 大小不符。Console.ReadKey() 等待)。掌握 GCHandle 與完成端口的交互細節,是建立高效能非同步 I/O 系統的基石。下一篇將介紹如何結合 Socket 實作真實的網路伺服器。