原文: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线程模型的更多相关文章

  1. C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包

    原文:C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包 守护线程 在服务端版Socket编程需要处理长时间没有发送数据的Socket,需要在超时多长时间后断开连接,我们 ...

  2. C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs封装

    原文:C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs封装 1.SocketAsyncEventArgs介绍 SocketAsyncEventArgs是微软提供的高性能 ...

  3. C#高性能大容量SOCKET并发(转)

    C#高性能大容量SOCKET并发(零):代码结构说明 C#高性能大容量SOCKET并发(一):IOCP完成端口例子介绍 C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs ...

  4. C#高性能大容量SOCKET并发(零):代码结构说明

    原文:C#高性能大容量SOCKET并发(零):代码结构说明 C#版完成端口具有以下特点: 连接在线管理(提供在线连接维护,连接会话管理,数据接收,连接断开等相关事件跟踪): 发送数据智能合并(组件会根 ...

  5. C#高性能大容量SOCKET并发(九):断点续传

    原文:C#高性能大容量SOCKET并发(九):断点续传 上传断点续传 断点续传主要是用在上传或下载文件,一般做法是开始上传的时候,服务器返回上次已经上传的大小,如果上传完成,则返回-1:下载开始的时候 ...

  6. C#高性能大容量SOCKET并发(三):接收、发送

    原文:C#高性能大容量SOCKET并发(三):接收.发送 异步数据接收有可能收到的数据不是一个完整包,或者接收到的数据超过一个包的大小,因此我们需要把接收的数据进行缓存.异步发送我们也需要把每个发送的 ...

  7. C#高性能大容量SOCKET并发(四):缓存设计

    原文:C#高性能大容量SOCKET并发(四):缓存设计 在编写服务端大并发的应用程序,需要非常注意缓存设计,缓存的设计是一个折衷的结果,需要通过并发测试反复验证.有很多服务程序是在启动时申请足够的内存 ...

  8. C#高性能大容量SOCKET并发(十一):编写上传客户端

    原文:C#高性能大容量SOCKET并发(十一):编写上传客户端 客户端封装整体框架 客户端编程基于阻塞同步模式,只有数据正常发送或接收才返回,如果发生错误则抛出异常,基于TcpClient进行封装,主 ...

  9. C#高性能大容量SOCKET并发(七):协议字符集

    原文:C#高性能大容量SOCKET并发(七):协议字符集 UTF-8 UTF-8是UNICODE的一种变长字符编码又称万国码,由Ken Thompson于1992年创建.现在已经标准化为RFC 362 ...

随机推荐

  1. ios9 xcode7以后编译需要进行的几项设置

    http://blog.csdn.net/hero82748274/article/details/48629461 1.库后缀变了:.dylib->tbd libsqlite3.0.dylib ...

  2. 基于 OAuth 安全协议的 Java 应用编程

    OAuth 简介 OAuth 是由 Blaine Cook.Chris Messina.Larry Halff 及 David Recordon 共同发起的,目的在于为 API 访问授权提供一个安全. ...

  3. XMPP之ios即时通讯客户端开发-配置XMPP基本信息之工程代码(五)

    登录功能完成以后包含以下代码文件: AppDelegate.h AppDelegate.m LoginViewController.h LoginViewController.m LoginUser. ...

  4. Watchdog机制概述

    1. Watchdog初始 Watchdog的中文的“看门狗”,有保护的意思.最早引入Watchdog是在单片机系统中,由于单片机的工作环境容易受到外界磁场的干扰,导致程序“跑飞”,造成整个系统无法正 ...

  5. 网站多语言转换.利用Google 语言的js.贴到网站就能用.

    <div id="google_translate_element"></div><script>function googleTranslat ...

  6. Qt 元对象系统(Meta-Object System)(不管是否使用信号槽,都推荐使用)

    Qt 元对象系统(Meta-Object System) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的对象提供了一个基类. 宏:Q_OBJECT,通常可以声明在类 ...

  7. Python 第三方库的安装

    1. pip 进入命令行,使用 pip install pip install numpy 2. 含有 setup.py 文件的第三方库 切换到 setup.py 所在的目录: python setu ...

  8. C# 与.NET2.0 中类型Type的GetMethod方法

    C#中类型Type有个GetMethod方法,调用该方法可获取指定方法名的方法信息实例. 使用时,其参数一般为2个,一个是方法名称字符串(可设置条件忽略大小写),另外一个参数为搜索方法的条件枚举. 该 ...

  9. Visual Studio 2012以后无法保存只读文件的问题

    https://stackoverflow.com/questions/12497216/visual-studio-2012-will-not-save-overwrite-read-only-fi ...

  10. 经典书单、站点 —— 大数据/数据分析/R语言

    1. 科普.入门 <大数据智能>,刘知远.崔安顺等著: 特色:系统,宏观和全面: 2. R 语言站点 http://langdawei.com/:R 语言数据采集与可视化: