C#高性能大容量SOCKET并发(十):SocketAsyncEventArgs线程模型

来源:互联网 发布:货币资金的数据来源于 编辑:程序博客网 时间:2024/05/16 06:18

C#高性能大容量SOCKET并发(十):SocketAsyncEventArgs线程模型

标签: 完成端口多线程并发高性能IOCP
 9623人阅读 评论(5) 收藏 举报
 分类:
 

线程模型

SocketAsyncEventArgs编程模式不支持设置同时工作线程个数,使用的NET的IO线程,由NET底层提供,这点和直接使用完成端口API编程不同。NET底层IO线程也是每个异步事件都是由不同的线程返回到Completed事件,因此在Completed事件需要对用户对象进行加锁,避免同一个用户对象同时触发两个Completed事件。

[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void IO_Completed(object sender, SocketAsyncEventArgs asyncEventArgs)  
  2. {  
  3.     AsyncSocketUserToken userToken = asyncEventArgs.UserToken as AsyncSocketUserToken;  
  4.     userToken.ActiveDateTime = DateTime.Now;  
  5.     try  
  6.     {                  
  7.         lock (userToken) //避免同一个userToken同时有多个线程操作  
  8.         {  
  9.             if (asyncEventArgs.LastOperation == SocketAsyncOperation.Receive)  
  10.                 ProcessReceive(asyncEventArgs);  
  11.             else if (asyncEventArgs.LastOperation == SocketAsyncOperation.Send)  
  12.                 ProcessSend(asyncEventArgs);  
  13.             else  
  14.                 throw new ArgumentException("The last operation completed on the socket was not a receive or send");  
  15.         }     
  16.     }  
  17.     catch (Exception E)  
  18.     {  
  19.         Program.Logger.ErrorFormat("IO_Completed {0} error, message: {1}", userToken.ConnectSocket, E.Message);  
  20.         Program.Logger.Error(E.StackTrace);  
  21.     }                       
  22. }  
使用ProceXP可以看到服务端在比较忙的时候,服务的线程会越多。在Completed事件加锁有好处是后续逻辑处理都是串行的,可以不用考虑线程同步。还有一个地方需要注意的是断开超时连接,由于超时连接会调用Shutdown函数来强行中断SOCKET,因此在守护线程调用时,也需要锁住userToken对象。

[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void DaemonThreadStart()  
  2. {  
  3.     while (m_thread.IsAlive)  
  4.     {  
  5.         AsyncSocketUserToken[] userTokenArray = null;  
  6.         m_asyncSocketServer.AsyncSocketUserTokenList.CopyList(ref userTokenArray);  
  7.         for (int i = 0; i < userTokenArray.Length; i++)  
  8.         {  
  9.             if (!m_thread.IsAlive)  
  10.                 break;  
  11.             try  
  12.             {  
  13.                 if ((DateTime.Now - userTokenArray[i].ActiveDateTime).Milliseconds > m_asyncSocketServer.SocketTimeOutMS) //超时Socket断开  
  14.                 {  
  15.                     lock (userTokenArray[i])  
  16.                     {  
  17.                         m_asyncSocketServer.CloseClientSocket(userTokenArray[i]);  
  18.                     }  
  19.                 }  
  20.             }                      
  21.             catch (Exception E)  
  22.             {  
  23.                 Program.Logger.ErrorFormat("Daemon thread check timeout socket error, message: {0}", E.Message);  
  24.                 Program.Logger.Error(E.StackTrace);  
  25.             }  
  26.         }  
  27.   
  28.         for (int i = 0; i < 60 * 1000 / 10; i++) //每分钟检测一次  
  29.         {  
  30.             if (!m_thread.IsAlive)  
  31.                 break;  
  32.             Thread.Sleep(10);  
  33.         }  
  34.     }  
  35. }  
在CloseClientSocket方法中,对m_asyncSocketUserTokenPool和m_asyncSocketUserTokenList进行处理的时候都有加锁,代码如下:

[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void CloseClientSocket(AsyncSocketUserToken userToken)  
  2. {  
  3.     if (userToken.ConnectSocket == null)  
  4.         return;  
  5.     string socketInfo = string.Format("Local Address: {0} Remote Address: {1}", userToken.ConnectSocket.LocalEndPoint,  
  6.         userToken.ConnectSocket.RemoteEndPoint);  
  7.     Program.Logger.InfoFormat("Client connection disconnected. {0}", socketInfo);  
  8.     try  
  9.     {  
  10.         userToken.ConnectSocket.Shutdown(SocketShutdown.Both);  
  11.     }  
  12.     catch (Exception E)   
  13.     {  
  14.         Program.Logger.ErrorFormat("CloseClientSocket Disconnect client {0} error, message: {1}", socketInfo, E.Message);  
  15.     }  
  16.     userToken.ConnectSocket.Close();  
  17.     userToken.ConnectSocket = null//释放引用,并清理缓存,包括释放协议对象等资源  
  18.   
  19.     m_maxNumberAcceptedClients.Release();  
  20.     m_asyncSocketUserTokenPool.Push(userToken);  
  21.     m_asyncSocketUserTokenList.Remove(userToken);  
  22. }  

[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void Push(AsyncSocketUserToken item)  
  2. {  
  3.     if (item == null)  
  4.     {  
  5.         throw new ArgumentException("Items added to a AsyncSocketUserToken cannot be null");  
  6.     }  
  7.     lock (m_pool)  
  8.     {  
  9.         m_pool.Push(item);  
  10.     }  
  11. }  

[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void Remove(AsyncSocketUserToken userToken)  
  2. {  
  3.     lock (m_list)  
  4.     {  
  5.         m_list.Remove(userToken);  
  6.     }  
  7. }  

在有些性能要求更高的系统,特别是在一些C++写的完成端口中,会使用原子操作来代替锁,这样做的好处是不用进行系统内核和用户态切换,性能会高。不过技术比较偏门,不易维护,而且实际表现需要进行多方面测试,这类优化更建议优化业务逻辑,并尽量减少内存分配和释放。


DEMO下载地址:http://download.csdn.net/detail/sqldebug_fan/7467745
免责声明:此代码只是为了演示C#完成端口编程,仅用于学习和研究,切勿用于商业用途。水平有限,C#也属于初学,错误在所难免,欢迎指正和指导。邮箱地址:fansheng_hx@163.com。

0 1
原创粉丝点击