我使用MSDN和(大多数)CodeProject中的示例来编写套接字服务器。我试图了解代码的线程安全性。所有套接字事件都会触发IO_Completed方法,该方法检查SAEA的最后操作类型(发送或接收):
void IO_Completed(object sender, SocketAsyncEventArgs e)
{
// determine which type of operation just completed and call the associated handler
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
考虑传入调用,ProcessReceive()需要完全是线程安全的,因为如果有很多客户端,它可能会在很短的时间内被多次调用,或者它会以某种方式阻塞,以便它在完成之前完全完成下一个事件再次调用它?我所做的不仅仅是将收到的消息直接反馈给客户端(这就是示例所做的)。
即使在示例中,ProcessReceive()也是一个很长的方法(见下文),肯定必须面临来自第二个线程的损坏风险。当我添加代码时,我需要做一些合理的事情(调用WCF服务),同样的代码再次运行的机会必须非常高。
我需要做些什么才能使ProcessReceive()(以及其他相关方法)通常是线程安全的,而不会影响使用SocketAsyncEventArgs所获得的性能?
下面的示例ProcessReceive()方法:
private void ProcessReceive(SocketAsyncEventArgs receiveSendEventArgs)
{
DataHoldingUserToken receiveSendToken =
(DataHoldingUserToken)receiveSendEventArgs.UserToken;
if (receiveSendEventArgs.SocketError != SocketError.Success)
{
receiveSendToken.Reset();
CloseClientSocket(receiveSendEventArgs);
return;
}
if (receiveSendEventArgs.BytesTransferred == 0)
{
receiveSendToken.Reset();
CloseClientSocket(receiveSendEventArgs);
return;
}
Int32 remainingBytesToProcess = receiveSendEventArgs.BytesTransferred;
if (receiveSendToken.receivedPrefixBytesDoneCount <
this.socketListenerSettings.ReceivePrefixLength)
{
remainingBytesToProcess = prefixHandler.HandlePrefix(receiveSendEventArgs,
receiveSendToken, remainingBytesToProcess);
if (remainingBytesToProcess == 0)
{
StartReceive(receiveSendEventArgs);
return;
}
}
bool incomingTcpMessageIsReady = messageHandler
.HandleMessage(receiveSendEventArgs,
receiveSendToken, remainingBytesToProcess);
if (incomingTcpMessageIsReady == true)
{
receiveSendToken.theMediator.HandleData(receiveSendToken.theDataHolder);
receiveSendToken.CreateNewDataHolder();
receiveSendToken.Reset();
receiveSendToken.theMediator.PrepareOutgoingData();
StartSend(receiveSendToken.theMediator.GiveBack());
}
else
{
receiveSendToken.receiveMessageOffset = receiveSendToken.bufferOffsetReceive;
receiveSendToken.recPrefixBytesDoneThisOp = 0;
StartReceive(receiveSendEventArgs);
}
}
答案 0 :(得分:1)
只需同步需要同步的内容。 IO_Completed
方法本身是线程安全无关的,不需要更改。
假设您的DataHoldingUserToken
(以及其他变量,例如prefixHandler
)不是线程安全的,那么它们将需要受到保护。据我所知,一个简单的lock
应该这样做。
心理模型是:IO_Completed
可以随时使用不同的参数调用;每个都在ThreadPool
线程上运行。
答案 1 :(得分:0)
我最近实施了类似的东西。它通过tcp连接处理消息。我创建了一个负责接受传入连接的线程。然后该线程将生成一个新线程来处理每个连接。这些线程在等待来自网络的I / O时被阻塞,因此它们不会占用CPU资源。如果您的连接不共享任何内容,则不需要线程安全。
答案 2 :(得分:0)
我建议使用异步编程模型,基本上客户端将调用BeginProcessReceive并传入回调,并在回调中执行EndProcessReceive。您可以使用任一任务,或者在4.0之前调用ThreadPool.QueueUserWorkItem。我猜这里,但看起来StartReceived或StartSend是阻塞方法,可以执行到他们自己的(线程池)线程。正如您所提到的,调用WCF服务将适合此模型。
除了各种其他优点之外,这个模型还可以让你处理大量的客户......