C# IOCP完成端口模型(简单实用高效)

1、在C#中,不用去面对完成端口的操作系统内核对象,Microsoft已经为我们提供了SocketAsyncEventArgs类,它封装了IOCP的使用。请参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-1。

2、我的SocketAsyncEventArgsPool类使用List对象来存储对客户端来通信的SocketAsyncEventArgs对象,它相当于直接使用内核对象时的IoContext。我这样设计比用堆栈来实现的好处理是,我可以在SocketAsyncEventArgsPool池中找到任何一个与服务器连接的客户,主动向它发信息。而用堆栈来实现的话,要主动给客户发信息,则还要设计一个结构来存储已连接上服务器的客户。

3、对每一个客户端不管还发送还是接收,我使用同一个SocketAsyncEventArgs对象,对每一个客户端来说,通信是同步进行的,也就是说服务器高度保证同一个客户连接上要么在投递发送请求,并等待;或者是在投递接收请求,等待中。本例只做echo服务器,还未考虑由服务器主动向客户发送信息。

4、SocketAsyncEventArgs的UserToken被直接设定为被接受的客户端Socket。

5、没有使用BufferManager 类,因为我在初始化时给每一个SocketAsyncEventArgsPool中的对象分配一个缓冲区,发送时使用Arrary.Copy来进行字符拷贝,不去改变缓冲区的位置,只改变使用的长度,因此在下次投递接收请求时恢复缓冲区长度就可以了!如果要主动给客户发信息的话,可以new一个SocketAsyncEventArgs对象,或者在初始化中建立几个来专门用于主动发送信息,因为这种需求一般是进行信息群发,建立一个对象可以用于很多次信息发送,总体来看,这种花销不大,还减去了字符拷贝和消耗。

demo下载地址: http://files.cnblogs.com/files/airtcp/IocpServer.zip
经典代码IOCP(完成端口)的C#.Net实现----解读

感谢原作者

经典代码IOCP(完成端口)的C#.Net实现

重复造轮子的意义在于深刻的理解和学习---

main.cs:

[csharp] view plain copy
print ?
  1. /*using System; 
  2. using System.Collections.Generic; 
  3. using System.Linq; 
  4. using System.Text; 
  5.  
  6.  
  7. namespace c2iocp 
  8. { 
  9.     class Program 
  10.     { 
  11.         static void Main(string[] args) 
  12.         { 
  13.         } 
  14.     } 
  15. } 
  16. */  
  17. using System;  
  18. using System.Threading;  // Included for the Thread.Sleep call  
  19. using Continuum.Threading;  
  20. namespace Sample  
  21. {  
  22.     //============================================  
  23.     /// <summary> Sample class for the threading class </summary>  
  24.     public class UtilThreadingSample  
  25.     {  
  26.         //*******************************************   
  27.         /// <summary> Test Method </summary>  
  28.         static void Main()  
  29.         {  
  30.             // Create the MSSQL IOCP Thread Pool  
  31.             IOCPThreadPool pThreadPool = new IOCPThreadPool(0, 5, 10, new IOCPThreadPool.USER_FUNCTION(IOCPThreadFunction));  
  32.             pThreadPool.PostEvent(10);  
  33.             Thread.Sleep(10000);  
  34.             pThreadPool.Dispose();  
  35.         }  
  36.         //*****************************************  
  37.         /// <summary> Function to be called by the IOCP thread pool.  Called when  
  38.         ///           a command is posted for processing by the SocketManager </summary>  
  39.         /// <param name="iValue"> The value provided by the thread posting the event </param>  
  40.         static public void IOCPThreadFunction(Int32 iValue)  
  41.         {  
  42.             try  
  43.             {  
  44.                 Console.WriteLine("Value: {0}", iValue);  
  45.             }  
  46.             catch (Exception pException)  
  47.             {  
  48.                 Console.WriteLine(pException.Message);  
  49.             }  
  50.         }  
  51.     }  
  52. }  
/*using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace c2iocp
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}
*/
using System;
using System.Threading;  // Included for the Thread.Sleep call
using Continuum.Threading;
namespace Sample
{
    //============================================
    /// <summary> Sample class for the threading class </summary>
    public class UtilThreadingSample
    {
        //******************************************* 
        /// <summary> Test Method </summary>
        static void Main()
        {
            // Create the MSSQL IOCP Thread Pool
            IOCPThreadPool pThreadPool = new IOCPThreadPool(0, 5, 10, new IOCPThreadPool.USER_FUNCTION(IOCPThreadFunction));
            pThreadPool.PostEvent(10);
            Thread.Sleep(10000);
            pThreadPool.Dispose();
        }
        //*****************************************
        /// <summary> Function to be called by the IOCP thread pool.  Called when
        ///           a command is posted for processing by the SocketManager </summary>
        /// <param name="iValue"> The value provided by the thread posting the event </param>
        static public void IOCPThreadFunction(Int32 iValue)
        {
            try
            {
                Console.WriteLine("Value: {0}", iValue);
            }
            catch (Exception pException)
            {
                Console.WriteLine(pException.Message);
            }
        }
    }
}

continuum.cs

[csharp] view plain copy
print ?
  1. using System;  
  2. using System.Threading;  
  3. using System.Runtime.InteropServices;  
  4.   
  5. namespace Continuum.Threading  
  6. {  
  7.   
  8.     // Structures  
  9.     //==========================================  
  10.     /// <summary> This is the WIN32 OVERLAPPED structure </summary>  
  11.     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]  
  12.     public unsafe struct OVERLAPPED  
  13.     {  
  14.         UInt32* ulpInternal;  
  15.         UInt32* ulpInternalHigh;  
  16.         Int32 lOffset;  
  17.         Int32 lOffsetHigh;  
  18.         UInt32 hEvent;  
  19.     }  
  20.   
  21.     // Classes  
  22.     //============================================  
  23.     /// <summary> This class provides the ability to create a thread pool to manage work.  The  
  24.     ///           class abstracts the Win32 IOCompletionPort API so it requires the use of  
  25.     ///           unmanaged code.  Unfortunately the .NET framework does not provide this functionality </summary>  
  26.     public sealed class IOCPThreadPool  
  27.     {  
  28.   
  29.         // Win32 Function Prototypes  
  30.         /// <summary> Win32Func: Create an IO Completion Port Thread Pool </summary>  
  31.         [DllImport("Kernel32", CharSet = CharSet.Auto)]  
  32.         private unsafe static extern UInt32 CreateIoCompletionPort(UInt32 hFile, UInt32 hExistingCompletionPort, UInt32* puiCompletionKey, UInt32 uiNumberOfConcurrentThreads);  
  33.   
  34.         /// <summary> Win32Func: Closes an IO Completion Port Thread Pool </summary>  
  35.         [DllImport("Kernel32", CharSet = CharSet.Auto)]  
  36.         private unsafe static extern Boolean CloseHandle(UInt32 hObject);  
  37.   
  38.         /// <summary> Win32Func: Posts a context based event into an IO Completion Port Thread Pool </summary>  
  39.         [DllImport("Kernel32", CharSet = CharSet.Auto)]  
  40.         private unsafe static extern Boolean PostQueuedCompletionStatus(UInt32 hCompletionPort, UInt32 uiSizeOfArgument, UInt32* puiUserArg, OVERLAPPED* pOverlapped);  
  41.   
  42.         /// <summary> Win32Func: Waits on a context based event from an IO Completion Port Thread Pool.  
  43.         ///           All threads in the pool wait in this Win32 Function </summary>  
  44.         [DllImport("Kernel32", CharSet = CharSet.Auto)]  
  45.         private unsafe static extern Boolean GetQueuedCompletionStatus(UInt32 hCompletionPort, UInt32* pSizeOfArgument, UInt32* puiUserArg, OVERLAPPED** ppOverlapped, UInt32 uiMilliseconds);  
  46.   
  47.         // Constants  
  48.         /// <summary> SimTypeConst: This represents the Win32 Invalid Handle Value Macro </summary>  
  49.         private const UInt32 INVALID_HANDLE_VALUE = 0xffffffff;  
  50.   
  51.         /// <summary> SimTypeConst: This represents the Win32 INFINITE Macro </summary>  
  52.         private const UInt32 INIFINITE = 0xffffffff;  
  53.   
  54.         /// <summary> SimTypeConst: This tells the IOCP Function to shutdown </summary>  
  55.         private const Int32 SHUTDOWN_IOCPTHREAD = 0x7fffffff;  
  56.   
  57.   
  58.   
  59.         // Delegate Function Types  
  60.         /// <summary> DelType: This is the type of user function to be supplied for the thread pool </summary>  
  61.         public delegate void USER_FUNCTION(Int32 iValue);  
  62.   
  63.   
  64.         // Private Properties  
  65.         private UInt32 m_hHandle;  
  66.         /// <summary> SimType: Contains the IO Completion Port Thread Pool handle for this instance </summary>  
  67.         private UInt32 GetHandle { get { return m_hHandle; } set { m_hHandle = value; } }  
  68.   
  69.         private Int32 m_uiMaxConcurrency;  
  70.         /// <summary> SimType: The maximum number of threads that may be running at the same time </summary>  
  71.         private Int32 GetMaxConcurrency { get { return m_uiMaxConcurrency; } set { m_uiMaxConcurrency = value; } }  
  72.   
  73.         private Int32 m_iMinThreadsInPool;  
  74.         /// <summary> SimType: The minimal number of threads the thread pool maintains </summary>  
  75.         private Int32 GetMinThreadsInPool { get { return m_iMinThreadsInPool; } set { m_iMinThreadsInPool = value; } }  
  76.   
  77.         private Int32 m_iMaxThreadsInPool;  
  78.         /// <summary> SimType: The maximum number of threads the thread pool maintains </summary>  
  79.         private Int32 GetMaxThreadsInPool { get { return m_iMaxThreadsInPool; } set { m_iMaxThreadsInPool = value; } }  
  80.   
  81.         private Object m_pCriticalSection;  
  82.         /// <summary> RefType: A serialization object to protect the class state </summary>  
  83.         private Object GetCriticalSection { get { return m_pCriticalSection; } set { m_pCriticalSection = value; } }  
  84.   
  85.         private USER_FUNCTION m_pfnUserFunction;  
  86.         /// <summary> DelType: A reference to a user specified function to be call by the thread pool </summary>  
  87.         private USER_FUNCTION GetUserFunction { get { return m_pfnUserFunction; } set { m_pfnUserFunction = value; } }  
  88.   
  89.         private Boolean m_bDisposeFlag;  
  90.         /// <summary> SimType: Flag to indicate if the class is disposing </summary>  
  91.         private Boolean IsDisposed { get { return m_bDisposeFlag; } set { m_bDisposeFlag = value; } }  
  92.   
  93.         // Public Properties  
  94.         private Int32 m_iCurThreadsInPool;  
  95.         /// <summary> SimType: The current number of threads in the thread pool </summary>  
  96.         public Int32 GetCurThreadsInPool { get { return m_iCurThreadsInPool; } set { m_iCurThreadsInPool = value; } }  
  97.         /// <summary> SimType: Increment current number of threads in the thread pool </summary>  
  98.         private Int32 IncCurThreadsInPool() { return Interlocked.Increment(ref m_iCurThreadsInPool); }  
  99.         /// <summary> SimType: Decrement current number of threads in the thread pool </summary>  
  100.         private Int32 DecCurThreadsInPool() { return Interlocked.Decrement(ref m_iCurThreadsInPool); }  
  101.         private Int32 m_iActThreadsInPool;  
  102.         /// <summary> SimType: The current number of active threads in the thread pool </summary>  
  103.         public Int32 GetActThreadsInPool { get { return m_iActThreadsInPool; } set { m_iActThreadsInPool = value; } }  
  104.         /// <summary> SimType: Increment current number of active threads in the thread pool </summary>  
  105.         private Int32 IncActThreadsInPool() { return Interlocked.Increment(ref m_iActThreadsInPool); }  
  106.         /// <summary> SimType: Decrement current number of active threads in the thread pool </summary>  
  107.         private Int32 DecActThreadsInPool() { return Interlocked.Decrement(ref m_iActThreadsInPool); }  
  108.         private Int32 m_iCurWorkInPool;  
  109.         /// <summary> SimType: The current number of Work posted in the thread pool </summary>  
  110.         public Int32 GetCurWorkInPool { get { return m_iCurWorkInPool; } set { m_iCurWorkInPool = value; } }  
  111.         /// <summary> SimType: Increment current number of Work posted in the thread pool </summary>  
  112.         private Int32 IncCurWorkInPool() { return Interlocked.Increment(ref m_iCurWorkInPool); }  
  113.         /// <summary> SimType: Decrement current number of Work posted in the thread pool </summary>  
  114.         private Int32 DecCurWorkInPool() { return Interlocked.Decrement(ref m_iCurWorkInPool); }  
  115.   
  116.   
  117.   
  118.         // Constructor, Finalize, and Dispose   
  119.         //***********************************************  
  120.         /// <summary> Constructor </summary>  
  121.         /// <param name = "iMaxConcurrency"> SimType: Max number of running threads allowed </param>  
  122.         /// <param name = "iMinThreadsInPool"> SimType: Min number of threads in the pool </param>  
  123.         /// <param name = "iMaxThreadsInPool"> SimType: Max number of threads in the pool </param>  
  124.         /// <param name = "pfnUserFunction"> DelType: Reference to a function to call to perform work </param>  
  125.         /// <exception cref = "Exception"> Unhandled Exception </exception>  
  126.         public IOCPThreadPool(Int32 iMaxConcurrency, Int32 iMinThreadsInPool, Int32 iMaxThreadsInPool, USER_FUNCTION pfnUserFunction)  
  127.         {  
  128.             try  
  129.             {  
  130.                 // Set initial class state  
  131.                 GetMaxConcurrency = iMaxConcurrency;//系统允许的最大线程数,如果为0,则和内核数一致,一般都是0,  
  132.                 GetMinThreadsInPool = iMinThreadsInPool;//最小线程数目  
  133.                 GetMaxThreadsInPool = iMaxThreadsInPool;//最大线程数目,都是设定给的参数  
  134.                 GetUserFunction = pfnUserFunction;//传递过来的回调函数,或者说事件处理函数  
  135.                 // Init the thread counters  
  136.                 GetCurThreadsInPool = 0;//当前线程池里的线程数  
  137.                 GetActThreadsInPool = 0;//活动的线程数,正在工作的线程数  
  138.                 GetCurWorkInPool = 0;//被投递的事件数目  
  139.                 // Initialize the Monitor Object  
  140.                 GetCriticalSection = new Object();//来包含类状态的对象????  
  141.                 // Set the disposing flag to false  
  142.                 IsDisposed = false;//是否资源释放标志  
  143.                 unsafe//c#调用外面dll的时候,需要用这个unsafe  
  144.                 {  
  145.                     // Create an IO Completion Port for Thread Pool use  
  146.                     //步骤1:创建iocp端口,  
  147.                     /*  
  148.                      * 参数说明编辑  
  149. FileHandle是有效的文件句柄或INVALID_HANDLE_VALUE。  
  150. ExistingCompletionPort是已经存在的完成端口。如果为NULL,则为新建一个IOCP。  
  151. CompletionKey是传送给处理函数的参数。  
  152. NumberOfConcurrentThreads是有多少个线程在访问这个消息队列。当参数ExistingCompletionPort不为0的时候,系统忽略该参数,当该参数为0表示允许同时相等数目于处理器个数的线程访问该消息队列。  
  153. 返回值编辑  
  154. 返回一个IOCP的句柄。若为NULL则创建失败,不为NULL则创建成功。  
  155.                      *   
  156.                      * CreateIoCompletionPort的前三个参数只在把设备同Complete Port相关联时才有用。  
  157. 此时我们只需传递INVALID_HANDLE_VALUE,NULL和0即可。  
  158. 第四个参数告诉端口同时能运行的最多线程数,这里设置为0,表示默认为当前计算机的CPU数目。  
  159.                      */  
  160.                     GetHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, null, (UInt32)GetMaxConcurrency);  
  161.                 }  
  162.                 // Test to make sure the IO Completion Port was created  
  163.                 if (GetHandle == 0)  
  164.                     throw new Exception("Unable To Create IO Completion Port");  
  165.                 // Allocate and start the Minimum number of threads specified  
  166.                 Int32 iStartingCount = GetCurThreadsInPool;//当前线程池里的线程数目  
  167.                 ThreadStart tsThread = new ThreadStart(IOCPFunction); 摘要: 表示在 System.Threading.Thread 上执行的方法。public delegate void ThreadStart();  
  168.                 /* 
  169.                  * 2 我们的ThreadFun线程函数执行一些初始化之后,将进入一个循环,该循环会在服务进程终止时才结束。 
  170.  在循环中,调用GetQueuedCompletionStatus,这样就把当前线程的ID放入一个等待线程队列中,I/O CP内核对象就总能知道哪个线程在等待处理完成的I/O请求。 
  171.  如果在IDLE_THREAD_TIMEOUT规定的时间内I/O CP上还没有出现一个Completion Packet,则转入下一次循环。在这里我们设置的IDLE_THREAD_TIMEOUT为1秒。 
  172.  当端口的I/O完成队列中出现一项时,完成端口就唤醒等待线程队列中的这个线程,该线程将得到完成的I/O项中的信息: 传输的字节数、完成键和OVERLAPPED结构的地址。 
  173.  在我们的程序中可以用智能指针或者BSTR或者int来接受这个OVERLAPPED结构的地址的值,从而得到消息;然后在这个线程中处理消息。 
  174.  GetQueuedCompletionStatus的第一个参数hCompletionPort指出了要监视哪一个端口,这里我们传送先前从CreateIoCompletionPort返回的端口句柄。 
  175.                  */  
  176.                 for (Int32 iThread = 0; iThread < GetMinThreadsInPool; ++iThread)  
  177.                 {  
  178.                     // Create a thread and start it  
  179.                     Thread thThread = new Thread(tsThread);  
  180.                     thThread.Name = "IOCP " + thThread.GetHashCode();  
  181.                     thThread.Start();  
  182.                     // Increment the thread pool count  
  183.                     IncCurThreadsInPool();//原子操作+1初始为0  
  184.                     /* 
  185.                      * long m_dwRefCount.. 
  186. InterlockedIncrement(&m_dwRefCount) 
  187. 对m_dwRefCount加1 
  188. 在对m_dwRefCount访问的时候其他线程不能访问这个变量 
  189. InterlockedIncrement实现的是原子性的加减,什么是原子性的加减呢? 
  190.  
  191. 举例:如果一个变量long value = 0; 
  192. 首先说一下正常情况下的加减操作,value+=1; 
  193. 1.系统从value的空间取出值,并动态生成一个空间存储取出来的值; 
  194. 2.将取出来的值和1做加法,并将和放入value的空间覆盖掉原值,操作结束; 
  195.                      
  196. 如果此时有2个Thread,分别记作AThread,BThread 
  197. 1.AThread将value从存储空间取出,为0; 
  198. 2.BThread将value从存储空间取出,为0; 
  199. 3.AThread将取出来的值和1做加法,并将和放入value的空间覆盖掉原值,加法结束,value = 1; 
  200. 4.BThread将取出来的值和1做加法,并将和放入value的空间覆盖掉原值,加法结束,value = 1; 
  201. 最后value应该是2,InterlockedIncrement保证一个线程访问变量时其他线程不能访问。 
  202.   
  203. 用于增减变量的并不是常用的Inc/Dec过程,而是用了InterlockedIncrement/InterlockedDecrement这一对过程,它们实现的功能完全一样,都是对变量加一或减一。 
  204. 但它们有一个最大的区别,那就是InterlockedIncrement/InterlockedDecrement是线程安全的。 
  205. 即它们在多线程下能保证执行结果正确,而Inc/Dec不能。 
  206. 或者按操作系统理论中的术语来说,这是一对“原语”操作。 
  207.                      */  
  208.                 }  
  209.             }  
  210.             catch  
  211.             {  
  212.                 throw new Exception("Unhandled Exception");  
  213.             }  
  214.         }  
  215.   
  216.         //***********************************************  
  217.         /// <summary> Finalize called by the GC </summary>  
  218.         ~IOCPThreadPool()  
  219.         {  
  220.             /*  
  221.              * 这个类结束,应该也意味着是主线程了,所以只有主线程访问?,既然是主线程,为什么还要加标志位呢????  
  222.              */  
  223.             if (!IsDisposed)//还没有释放资源,则释放,这个应该也是原子操作对isdisposeD吧  
  224.                 Dispose();  
  225.         }  
  226.   
  227.         //**********************************************  
  228.         /// <summary> Called when the object will be shutdown.  This  
  229.         ///           function will wait for all of the work to be completed  
  230.         ///           inside the queue before completing </summary>  
  231.         public void Dispose()  
  232.         {  
  233.             try  
  234.             {  
  235.                 // Flag that we are disposing this object  
  236.                 IsDisposed = true;  
  237.                 // Get the current number of threads in the pool  
  238.                 Int32 iCurThreadsInPool = GetCurThreadsInPool;//现在生成的线程数,用原子操作实现的  
  239.                 // Shutdown all thread in the pool  
  240.                 for (Int32 iThread = 0; iThread < iCurThreadsInPool; ++iThread)  
  241.                 {  
  242.                     unsafe  
  243.                     {  
  244.                         bool bret = PostQueuedCompletionStatus(GetHandle, 4, (UInt32*)SHUTDOWN_IOCPTHREAD, null);  
  245.                         /*  
  246.                          * PostQueuedCompletionStatus函数,向每个工作者线程都发送—个特殊的完成数据包。该函数会指示每个线程都“立即结束并退出”.  
  247.                          * 下面是PostQueuedCompletionStatus函数的定义:   
  248. [cpp] view plain copy  
  249. BOOL PostQueuedCompletionStatus(     
  250.     HANDLE CompletlonPort,     
  251.     DW0RD dwNumberOfBytesTrlansferred,     
  252.     DWORD dwCompletlonKey,     
  253. LPOVERLAPPED lpoverlapped,    
  254. );    
  255.   
  256.   
  257.        其中,CompletionPort参数指定想向其发送一个完成数据包的完成端口对象。而就dwNumberOfBytesTransferred,dwCompletionKey和lpOverlapped  
  258.                          * 这三个参数来说.每—个都允许我们指定—个值,直接传递给GetQueuedCompletionStatus函数中对应的参数。这样—来。  
  259.                          * —个工作者线程收到传递过来的三个GetQueuedCompletionStatus函数参数后,便可根据由这三个参数的某一个设置的特殊值,  
  260.                          * 决定何时应该退出。例如,可用dwCompletionPort参数传递0值,而—个工作者线程会将其解释成中止指令。一旦所有工作者线程都已关闭,  
  261.                          * 便可使用CloseHandle函数,关闭完成端口。最终安全退出程序。   
  262.         PostQueuedCompletionStatus函数提供了一种方式来与线程池中的所有线程进行通信。如,当用户终止服务应用程序时,我们想要所有线程都完全利索地退出。  
  263.                          * 但是如果各线程还在等待完成端口而又没有已完成的I/O 请求,那么它们将无法被唤醒。   
  264.        通过为线程池中的每个线程都调用一次PostQueuedCompletionStatus,我们可以将它们都唤醒。每个线程会对GetQueuedCompletionStatus的返回值进行检查,  
  265.                          * 如果发现应用程序正在终止,那么它们就可以进行清理工作并正常地退出。  
  266.                          */  
  267.                     }  
  268.                 }  
  269.                 // Wait here until all the threads are gone  
  270.                 while (GetCurThreadsInPool != 0) Thread.Sleep(100);  
  271.                 unsafe  
  272.                 {  
  273.                     // Close the IOCP Handle  
  274.                     CloseHandle(GetHandle);//便可使用CloseHandle函数,关闭完成端口。最终安全退出程序。   
  275.                 }  
  276.             }  
  277.             catch  
  278.             {  
  279.             }  
  280.         }  
  281.   
  282.         // Private Methods  
  283.         //*******************************************  
  284.         /// <summary> IOCP Worker Function that calls the specified user function </summary>  
  285.         private void IOCPFunction()  
  286.         {  
  287.             UInt32 uiNumberOfBytes;  
  288.             Int32 iValue;  
  289.             try  
  290.             {  
  291.                 while (true)  
  292.                 {  
  293.                     unsafe  
  294.                     {  
  295.                         OVERLAPPED* pOv;  
  296.                         // Wait for an event  
  297.                         /*  
  298.                          * BOOL GetQueuedCompletionStatus(  
  299. HANDLE CompletionPort,  
  300. LPDWORD lpNumberOfBytes,  
  301. PULONG_PTR lpCompletionKey,  
  302. LPOVERLAPPED *lpOverlapped,  
  303. DWORD dwMilliseconds);  
  304. 调用参数:  
  305. CompletionPort:指定的IOCP,该值由CreateIoCompletionPort函数创建。  
  306. lpnumberofbytes:一次完成后的I/O操作所传送数据的字节数。  
  307. lpcompletionkey:当文件I/O操作完成后,用于存放与之关联的CK。  
  308. lpoverlapped:为调用IOCP机制所引用的OVERLAPPED结构。  
  309. dwmilliseconds:用于指定调用者等待CP的时间。  
  310. 返回值:  
  311. 调用成功,则返回非零数值,相关数据存于lpNumberOfBytes、lpCompletionKey、lpoverlapped变量中。失败则返回零值。  
  312.                          */  
  313.                         GetQueuedCompletionStatus(GetHandle, &uiNumberOfBytes, (UInt32*)&iValue, &pOv, INIFINITE);  
  314.                     }  
  315.                     // Decrement the number of events in queue  
  316.                     DecCurWorkInPool();//原子操作,减少一个io里面的任务数目  
  317.                     // Was this thread told to shutdown  
  318.                     if (iValue == SHUTDOWN_IOCPTHREAD)//关闭标志位,post传递一个io完成数据,这里读到,知道该结束了,提前约定  
  319.                         break;  
  320.                     // Increment the number of active threads  
  321.                     IncActThreadsInPool();//这个线程读到任务开始工作,增加活动线程数  
  322.                     try  
  323.                     {  
  324.                         // Call the user function  
  325.                         GetUserFunction(iValue);  
  326.                     }  
  327.                     catch  
  328.                     {  
  329.                     }  
  330.                     // Get a lock  
  331.                     Monitor.Enter(GetCriticalSection);  
  332.                     try  
  333.                     {  
  334.                         // If we have less than max threads currently in the pool  
  335.                         //当前已经生成了符合设定最小线程数目的线程,如果当前每到最大线程数,继续创建  
  336.                         if (GetCurThreadsInPool < GetMaxThreadsInPool)  
  337.                         {  
  338.                             // Should we add a new thread to the pool  
  339.                             //所有线程都已在工作  
  340.                             if (GetActThreadsInPool == GetCurThreadsInPool)  
  341.                             {  
  342.                                 if (IsDisposed == false)//没有进行资源释放,标志位的作用在这里  
  343.                                 {  
  344.                                     // Create a thread and start it  
  345.                                     ThreadStart tsThread = new ThreadStart(IOCPFunction);  
  346.                                     Thread thThread = new Thread(tsThread);  
  347.                                     thThread.Name = "IOCP " + thThread.GetHashCode();  
  348.                                     thThread.Start();  
  349.                                     // Increment the thread pool count  
  350.                                     IncCurThreadsInPool();//增加当前的活动总数,子线程还可以继续生成线程????  
  351.                                 }  
  352.                             }  
  353.                         }  
  354.                     }  
  355.                     catch  
  356.                     {  
  357.                     }  
  358.                     // Relase the lock  
  359.                     Monitor.Exit(GetCriticalSection);//这个是为了保证,比如当前进程为5都在工作了,最大上限设定10,  
  360.                     //发现都在工作只增加1个额外空闲线程,如果不用 Monitor 是不是会都发现不够一下增加太多,最后可能当前线程数超过10  
  361.                     // Increment the number of active threads  
  362.                     DecActThreadsInPool();//干活结束,减少忙碌状态的线程  
  363.                 }  
  364.             }  
  365.             catch  
  366.             {  
  367.             }  
  368.             // Decrement the thread pool count  
  369.             DecCurThreadsInPool();//线程退出,减少当前进程数  
  370.         }  
  371.   
  372.         // Public Methods  
  373.         //******************************************  
  374.         /// <summary> IOCP Worker Function that calls the specified user function </summary>  
  375.         /// <param name="iValue"> SimType: A value to be passed with the event </param>  
  376.         /// <exception cref = "Exception"> Unhandled Exception </exception>  
  377.         public void PostEvent(Int32 iValue)  
  378.         {  
  379.             try  
  380.             {  
  381.                 // Only add work if we are not disposing  
  382.                 if (IsDisposed == false)  
  383.                 {  
  384.                     unsafe  
  385.                     {  
  386.                         // Post an event into the IOCP Thread Pool  
  387.                         PostQueuedCompletionStatus(GetHandle, 4, (UInt32*)iValue, null);  
  388.                     }  
  389.                     // Increment the number of item of work  
  390.                     IncCurWorkInPool();  
  391.                     // Get a lock  
  392.                     Monitor.Enter(GetCriticalSection);  
  393.                     try  
  394.                     {  
  395.                         // If we have less than max threads currently in the pool  
  396.                         if (GetCurThreadsInPool < GetMaxThreadsInPool)  
  397.                         {  
  398.                             // Should we add a new thread to the pool  
  399.                             if (GetActThreadsInPool == GetCurThreadsInPool)  
  400.                             {  
  401.                                 if (IsDisposed == false)  
  402.                                 {  
  403.                                     // Create a thread and start it  
  404.                                     ThreadStart tsThread = new ThreadStart(IOCPFunction);  
  405.                                     Thread thThread = new Thread(tsThread);  
  406.                                     thThread.Name = "IOCP " + thThread.GetHashCode();  
  407.                                     thThread.Start();  
  408.                                     // Increment the thread pool count  
  409.                                     IncCurThreadsInPool();  
  410.                                 }  
  411.                             }  
  412.                         }  
  413.                     }  
  414.                     catch  
  415.                     {  
  416.                     }  
  417.                     // Release the lock  
  418.                     Monitor.Exit(GetCriticalSection);  
  419.                 }  
  420.             }  
  421.             catch (Exception e)  
  422.             {  
  423.                 throw e;  
  424.             }  
  425.             catch  
  426.             {  
  427.                 throw new Exception("Unhandled Exception");  
  428.             }  
  429.         }  
  430.         //*****************************************  
  431.         /// <summary> IOCP Worker Function that calls the specified user function </summary>  
  432.         /// <exception cref = "Exception"> Unhandled Exception </exception>  
  433.         public void PostEvent()  
  434.         {  
  435.             try  
  436.             {  
  437.                 // Only add work if we are not disposing  
  438.                 if (IsDisposed == false)  
  439.                 {  
  440.                     unsafe  
  441.                     {  
  442.                         // Post an event into the IOCP Thread Pool  
  443.                         PostQueuedCompletionStatus(GetHandle, 0, nullnull);  
  444.                     }  
  445.                     // Increment the number of item of work  
  446.                     IncCurWorkInPool();  
  447.                     // Get a lock  
  448.                     Monitor.Enter(GetCriticalSection);  
  449.                     try  
  450.                     {  
  451.                         // If we have less than max threads currently in the pool  
  452.                         if (GetCurThreadsInPool < GetMaxThreadsInPool)  
  453.                         {  
  454.                             // Should we add a new thread to the pool  
  455.                             if (GetActThreadsInPool == GetCurThreadsInPool)  
  456.                             {  
  457.                                 if (IsDisposed == false)  
  458.                                 {  
  459.                                     // Create a thread and start it  
  460.                                     ThreadStart tsThread = new ThreadStart(IOCPFunction);  
  461.                                     Thread thThread = new Thread(tsThread);  
  462.                                     thThread.Name = "IOCP " + thThread.GetHashCode();  
  463.                                     thThread.Start();  
  464.                                     // Increment the thread pool count  
  465.                                     IncCurThreadsInPool();  
  466.                                 }  
  467.                             }  
  468.                         }  
  469.                     }  
  470.                     catch  
  471.                     {  
  472.                     }  
  473.                     // Release the lock  
  474.                     Monitor.Exit(GetCriticalSection);  
  475.                 }  
  476.             }  
  477.             catch (Exception e)  
  478.             {  
  479.                 throw e;  
  480.             }  
  481.             catch  
  482.             {  
  483.                 throw new Exception("Unhandled Exception");  
  484.             }  
  485.         }  
  486.     }  
  487. }  
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、在C#中,不用去面对完成端口的操作系统内核对象,Microsoft已经为我们提供了SocketAsyncEventArgs类,它封装了IOCP的使用。请参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-1。 2、我的SocketAsyncEventArgsPool类使用List对象来存储对客户端来通信的SocketAsyncEventArgs对象,它相当于直接使用内核对象时的IoContext。我这样设计比用堆栈来实现的好处理是,我可以在SocketAsyncEventArgsPool池中找到任何一个与服务器连接的客户,主动向它发信息。而用堆栈来实现的话,要主动给客户发信息,则还要设计一个结构来存储已连接上服务器的客户。 3、对每一个客户端不管还发送还是接收,我使用同一个SocketAsyncEventArgs对象,对每一个客户端来说,通信是同步进行的,也就是说服务器高度保证同一个客户连接上要么在投递发送请求,并等待;或者是在投递接收请求,等待中。本例只做echo服务器,还未考虑由服务器主动向客户发送信息。 4、SocketAsyncEventArgs的UserToken被直接设定为被接受的客户端Socket。 5、没有使用BufferManager 类,因为我在初始化时给每一个SocketAsyncEventArgsPool中的对象分配一个缓冲区,发送时使用Arrary.Copy来进行字符拷贝,不去改变缓冲区的位置,只改变使用的长度,因此在下次投递接收请求时恢复缓冲区长度就可以了!如果要主动给客户发信息的话,可以new一个SocketAsyncEventArgs对象,或者在初始化中建立几个来专门用于主动发送信息,因为这种需求一般是进行信息群发,建立一个对象可以用于很多次信息发送,总体来看,这种花销不大,还减去了字符拷贝和消耗。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值