C#高性能大容量SOCKET并发(十):SocketAsyncEventArgs线程模型
原文:C#高性能大容量SOCKET并发(十):SocketAsyncEventArgs线程模型
线程模型
SocketAsyncEventArgs编程模式不支持设置同时工作线程个数,使用的NET的IO线程,由NET底层提供,这点和直接使用完成端口API编程不同。NET底层IO线程也是每个异步事件都是由不同的线程返回到Completed事件,因此在Completed事件需要对用户对象进行加锁,避免同一个用户对象同时触发两个Completed事件。
void IO_Completed(object sender, SocketAsyncEventArgs asyncEventArgs)
{
AsyncSocketUserToken userToken = asyncEventArgs.UserToken as AsyncSocketUserToken;
userToken.ActiveDateTime = DateTime.Now;
try
{
lock (userToken) //避免同一个userToken同时有多个线程操作
{
if (asyncEventArgs.LastOperation == SocketAsyncOperation.Receive)
ProcessReceive(asyncEventArgs);
else if (asyncEventArgs.LastOperation == SocketAsyncOperation.Send)
ProcessSend(asyncEventArgs);
else
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
catch (Exception E)
{
Program.Logger.ErrorFormat("IO_Completed {0} error, message: {1}", userToken.ConnectSocket, E.Message);
Program.Logger.Error(E.StackTrace);
}
}使用ProceXP可以看到服务端在比较忙的时候,服务的线程会越多。在Completed事件加锁有好处是后续逻辑处理都是串行的,可以不用考虑线程同步。还有一个地方需要注意的是断开超时连接,由于超时连接会调用Shutdown函数来强行中断SOCKET,因此在守护线程调用时,也需要锁住userToken对象。
public void DaemonThreadStart()
{
while (m_thread.IsAlive)
{
AsyncSocketUserToken[] userTokenArray = null;
m_asyncSocketServer.AsyncSocketUserTokenList.CopyList(ref userTokenArray);
for (int i = 0; i < userTokenArray.Length; i++)
{
if (!m_thread.IsAlive)
break;
try
{
if ((DateTime.Now - userTokenArray[i].ActiveDateTime).Milliseconds > m_asyncSocketServer.SocketTimeOutMS) //超时Socket断开
{
lock (userTokenArray[i])
{
m_asyncSocketServer.CloseClientSocket(userTokenArray[i]);
}
}
}
catch (Exception E)
{
Program.Logger.ErrorFormat("Daemon thread check timeout socket error, message: {0}", E.Message);
Program.Logger.Error(E.StackTrace);
}
} for (int i = 0; i < 60 * 1000 / 10; i++) //每分钟检测一次
{
if (!m_thread.IsAlive)
break;
Thread.Sleep(10);
}
}
}在CloseClientSocket方法中,对m_asyncSocketUserTokenPool和m_asyncSocketUserTokenList进行处理的时候都有加锁,代码如下:
public void CloseClientSocket(AsyncSocketUserToken userToken)
{
if (userToken.ConnectSocket == null)
return;
string socketInfo = string.Format("Local Address: {0} Remote Address: {1}", userToken.ConnectSocket.LocalEndPoint,
userToken.ConnectSocket.RemoteEndPoint);
Program.Logger.InfoFormat("Client connection disconnected. {0}", socketInfo);
try
{
userToken.ConnectSocket.Shutdown(SocketShutdown.Both);
}
catch (Exception E)
{
Program.Logger.ErrorFormat("CloseClientSocket Disconnect client {0} error, message: {1}", socketInfo, E.Message);
}
userToken.ConnectSocket.Close();
userToken.ConnectSocket = null; //释放引用,并清理缓存,包括释放协议对象等资源 m_maxNumberAcceptedClients.Release();
m_asyncSocketUserTokenPool.Push(userToken);
m_asyncSocketUserTokenList.Remove(userToken);
}public void Push(AsyncSocketUserToken item)
{
if (item == null)
{
throw new ArgumentException("Items added to a AsyncSocketUserToken cannot be null");
}
lock (m_pool)
{
m_pool.Push(item);
}
}public void Remove(AsyncSocketUserToken userToken)
{
lock (m_list)
{
m_list.Remove(userToken);
}
}在有些性能要求更高的系统,特别是在一些C++写的完成端口中,会使用原子操作来代替锁,这样做的好处是不用进行系统内核和用户态切换,性能会高。不过技术比较偏门,不易维护,而且实际表现需要进行多方面测试,这类优化更建议优化业务逻辑,并尽量减少内存分配和释放。
DEMO下载地址:http://download.csdn.net/detail/sqldebug_fan/7467745
免责声明:此代码只是为了演示C#完成端口编程,仅用于学习和研究,切勿用于商业用途。水平有限,C#也属于初学,错误在所难免,欢迎指正和指导。邮箱地址:fansheng_hx@163.com。
C#高性能大容量SOCKET并发(十):SocketAsyncEventArgs线程模型的更多相关文章
- C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包
原文:C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包 守护线程 在服务端版Socket编程需要处理长时间没有发送数据的Socket,需要在超时多长时间后断开连接,我们 ...
- C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs封装
原文:C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs封装 1.SocketAsyncEventArgs介绍 SocketAsyncEventArgs是微软提供的高性能 ...
- C#高性能大容量SOCKET并发(转)
C#高性能大容量SOCKET并发(零):代码结构说明 C#高性能大容量SOCKET并发(一):IOCP完成端口例子介绍 C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs ...
- C#高性能大容量SOCKET并发(零):代码结构说明
原文:C#高性能大容量SOCKET并发(零):代码结构说明 C#版完成端口具有以下特点: 连接在线管理(提供在线连接维护,连接会话管理,数据接收,连接断开等相关事件跟踪): 发送数据智能合并(组件会根 ...
- C#高性能大容量SOCKET并发(九):断点续传
原文:C#高性能大容量SOCKET并发(九):断点续传 上传断点续传 断点续传主要是用在上传或下载文件,一般做法是开始上传的时候,服务器返回上次已经上传的大小,如果上传完成,则返回-1:下载开始的时候 ...
- C#高性能大容量SOCKET并发(三):接收、发送
原文:C#高性能大容量SOCKET并发(三):接收.发送 异步数据接收有可能收到的数据不是一个完整包,或者接收到的数据超过一个包的大小,因此我们需要把接收的数据进行缓存.异步发送我们也需要把每个发送的 ...
- C#高性能大容量SOCKET并发(四):缓存设计
原文:C#高性能大容量SOCKET并发(四):缓存设计 在编写服务端大并发的应用程序,需要非常注意缓存设计,缓存的设计是一个折衷的结果,需要通过并发测试反复验证.有很多服务程序是在启动时申请足够的内存 ...
- C#高性能大容量SOCKET并发(十一):编写上传客户端
原文:C#高性能大容量SOCKET并发(十一):编写上传客户端 客户端封装整体框架 客户端编程基于阻塞同步模式,只有数据正常发送或接收才返回,如果发生错误则抛出异常,基于TcpClient进行封装,主 ...
- C#高性能大容量SOCKET并发(七):协议字符集
原文:C#高性能大容量SOCKET并发(七):协议字符集 UTF-8 UTF-8是UNICODE的一种变长字符编码又称万国码,由Ken Thompson于1992年创建.现在已经标准化为RFC 362 ...
随机推荐
- 分布式ID解决方案
开发十年,就只剩下这套Java开发体系了 >>> 在游戏开发中,我们使用分布式ID.有很多优点 便于合服 便于ID管理 等等 一.单服各自ID系统的弊端 1. 列如合服 在游戏上 ...
- JDBC连接数据库步骤及Class.forName()(转)
JDBC连接数据库 JDBC是Sun公司制定的一个可以用Java语言连接数据库的技术. 一.JDBC基础知识 JDBC(Java DataBase Connectivity,java数据库连接)是一种 ...
- 卷积与反卷积、步长(stride)与重叠(overlap)
1. 卷积与反卷积 如上图演示了卷积核反卷积的过程,定义输入矩阵为 I(4×4),卷积核为 K(3×3),输出矩阵为 O(2×2): 卷积的过程为:Conv(I,W)=O 反卷积的过称为:Deconv ...
- iOS6 与iOS7以及7以上状态栏的颜色设置
iOS7默认状态栏文字颜色为黑色 修改为白色的方法:(chenyong注意 我的Status bar style 使用的仍是默认值Gray style(default)) 1在Info.plist中设 ...
- VS2017 安装过程
2017 安装过程 工欲善其事必先利其器 Visual Studio 2017 正式版官方下载地址:https://www.visualstudio.com/downloads/ 安装vs2017的时 ...
- 使用elasticsearch遇到的一些问题以及解决方法(不断更新)
7.org.elasticsearch.transport.RemoteTransportException: Failed to deserialize exception response fro ...
- [ACM] POJ 2689 Prime Distance (筛选范围大素数)
Prime Distance Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 12811 Accepted: 3420 D ...
- NVIDIA 显卡与 CUDA 在深度学习中的应用
CUDA(Compute Unified Device Architecture),是显卡厂商 NVIDIA 推出的运算平台. 0. 配置 显卡驱动的下载地址:Drivers - Download N ...
- Delphi MD5加密
Delphi MD5加密 1. 引用两个单元 uses IdHash,IdHashMessageDigest; 2.编写加密函数 function TEncrypti ...
- 手把手教你安装QT集成开发环境(操作系统为ubuntu10.04,需要先安装build-essential和libncurses5-dev)
在安装QT集成开发工具包之前需要先安装build-essential和libncurses5-dev这两个开发工具和库,libncurses5-dev库是一个在Linux/Unix下广泛应用的图形函数 ...