程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 在c#使用IOCP(完成端口)的簡單示例(1)

在c#使用IOCP(完成端口)的簡單示例(1)

編輯:關於C語言

這次給大家演示一下利用IOCP的在線程間傳遞數據的例子,順便打算講一些細節和注意的地方。

概述:這裡主要使用IOCP的三個API,CreateIoCompletionPort,PostQueuedCompletionStatus,GetQueuedCompletionStatus,第一個是用來創建一個完成端口對象,第二個是向一個端口發送數據,第三個是接受數據,基本上用著三個函數,就可以寫一個使用IOCP的簡單示例。

其中完成端口一個內核對象,所以創建的時候會耗費性能,CPU得切換到內核模式,而且一旦創建了內核對象,我們都要記著要不用的時候顯式的釋放它的句柄,釋放非托管資源的最佳實踐肯定是使用Dispose模式,這個博客園有人講過N次了。而一般要獲取一個內核對象的引用,最好用SafeHandle來引用它,這個類可以幫你管理引用計數,而且用它引用內核對象,代碼更健壯,如果用指針引用內核對象,在創建成功內核對象並復制給指針這個時間段,如果拋了ThreadAbortException,這個內核對象就洩漏了,而用SafeHandle去應用內核對象就不會在賦值的時候發生ThreadAbortException。另外SafeHandle類繼承自CriticalFinalizerObject類,並實現了IDispose接口,CLR對CriticalFinalizerObject及其子類有特殊照顧,比如說在編譯的時候優先編譯,在調用非CriticalFinalizerObject類的Finalize方法後再調用CriticalFinalizerObject類的Finalize類的Finalize方法等。在win32裡,一般一個句柄是-1或者0的時候表示這個句柄是無效的,所以.net有一個SafeHandle的派生類SafeHandleZeroOrMinusOneIsInvalid ,但是這個類是一個抽象類,你要引用自己使用的內核對象或者非托管對象,要從這個類派生一個類並重寫Relseas方法。另外在.Net框架裡它有兩個實現幾乎一模一樣的子類,一個是SafeFileHandle一個是SafeWaitHandle,前者表示文件句柄,後者表示等待句柄,我們這裡為了方便就直接用SafeFileHandle來引用完成端口對象了。

CreateIoCompletionPort函數的原型如下 [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern SafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey, uint NumberOfConcurrentThreads);

FileHandle參數表示要綁定在完成端口上的句柄,比如說一個已經accept的socket句柄。

ExistingCompletionPort參數表示一個已有的完成端口句柄,第一次創建完成端口的時候顯然隨便傳個值就行,所以這個參數直接定義成IntPtr類型了。當你創建了工作線程來為I/O請求服務的時候,才會把句柄和完成端口關聯在一起,而之前第一次創建完成端口的時候這個參數傳一個zero指針就O了,而FileHandle參數傳一個-1的指針就行了。

CompletionKey是完成鍵的意思,它可以是任意想傳遞給工作線程的數據,學名叫做單句柄數據,就是說跟隨FileHandle參數走的一些狀態數據,一般在socket的iocp程序裡是把socket傳進去,以便在工作線程裡拿到這個socket句柄,在收到異步操作完成的通知及處理後繼續進行下一個異步操作的投遞,如發送和接受數據等。

NumberOfConcurrentThreads參數表示在一個完成端口上同時允許執行的最大線程數量。如果傳0,就是說你有幾個CPU,就是允許最大有幾個線程,這也是最理想情況,因為一個CPU一個線程可以防止線程上下文切換。關於這個值要和創建工作線程的數量的關系,大家要理解清楚,不一定CPU有多少個,你的工作線程就創建多少個。因為你的工作線程有時候會阻塞或者等待,而如果你正好創建了CPU個數個工作線程,有一個等待的話,因為你分配了同時最多有CPU個數多個最大IOCP線程,這時候就不能效率最大化了。所以一般工作線程創建的要比CPU個數多一些,除非你保證你的工作線程不會阻塞。

PostQueuedCompletionStatus函數原型如下 [DllImport("Kernel32", CharSet = CharSet.Auto)]   private static extern bool PostQueuedCompletionStatus(SafeFileHandle CompletionPort, uint dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped); 該方法用於給完成端口投遞自定義信息,一般情況下如果把某個句柄和完成端口綁定後,當有數據收發操作完成時會自動同時工作線程,工作線程裡的GetQueuedCompletionStatus就不會阻塞,而繼續往下走,來進行接收到IO操作完成通知的流程。而有時候我們需要手工向工作者線程投遞一些消息,比如說我們主線程知道所有的socket句柄都關閉了,工作線程可以退出了,我們就可以給工作線程發一個自定義數據,工作線程收到後判斷是否是退出指令,然後退出。

CompletionPort參數表示向哪個完成端口對象投遞信息,在這個完成端口上等待消息的工作線程就會收到消息了。 dwNumberOfBytesTransferred表示你投遞的數據有多大,我們一般投遞的是一個對象的指針,在32位系統裡,int指針就是4個字節了,直接寫4就O了,要不就用sizeof你傳的數據,如sizeof(IntPtr)。

dwCompletionKey同CreateIoCompletionPort的解釋,是單句柄數據,本示例用不到,不細說,直接用IntPtr.Zero填充了事。

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