日期:2014-05-16 浏览次数:21397 次
线程模型
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.Conn