我的TcpServer中有以下方法
private void Receive(ZClient client)
{
NetworkStream ns = client.TcpClient.GetStream();
int bytesRead = 0;
byte[] message = new byte[client.TcpClient.ReceiveBufferSize];
while (true)
{
bytesRead = ns.Read(message, 0, client.TcpClient.ReceiveBufferSize);
if (bytesRead == 0)
{
break; // Exit Thread
}
else
{
Array.Resize<Byte>(ref client.Buffer, client.Buffer.Length + bytesRead);
Buffer.BlockCopy(message, 0, client.Buffer, client.Buffer.Length - bytesRead, bytesRead);
// Issue Below
DisassembleRawMessage(client); // *1 See below
new Thread(()=>DeQueueMessages(client)).Start(); // *2 See below
// End Issue
}
}
}
DissassembleRawMessage负责处理我的缓冲区并在我的客户端消息队列中输入单个消息。没问题。
DeQueueMessages负责处理以前放在DissassembleRawMessage方法中的客户端消息队列中的对象。
这个问题看起来很明显。收到消息后,我会关闭一个新线程。在对各个消息进行解除和处理的过程中,队列可能会添加更多项目,因为可能已在中间接收到更多数据。这会导致枚举错误,提示我在枚举期间不能修改集合的内容。
我不想通过在同一个线程内处理队列来阻止我的接收方法,因为它会破坏允许队列建立请求的目的,同时仍然接收数据。如何继续排队对象,同时处理队列中的对象而不会遇到枚举修改异常?
更新:使消息出列的方法。
while (client.Queue.Count() > 0)
{
byte[] buffer = client.Queue.Dequeue();
...
}
答案 0 :(得分:2)
首先,我不会为收到的每条消息使用新线程。要么启动等待新工作而不是退出的线程,要么使用ThreadPool。
如果在枚举期间遇到InvalidOperationException,则表示您正在迭代列表或其他IEnumerable。这是否意味着你正在做这样的事情?
void DeQueueMessages(ZClient client)
{
foreach(Message m in client.messageList)
{
...
}
messageList.Clear();
}
如果是这样,切换到使用实际的Queue类,并在循环时执行此操作:
void DeQueueMessages(ZClient client)
{
while(client.messageQueue.Count > 0)
{
Message m = client.messageQueue.Dequeue();
...
}
}
如果内存服务,标准的System.Collections.Generic.Queue类不是线程安全的,所以我选择System.Collections.Concurrent.ConcurrentQueue。
答案 1 :(得分:1)
异步I / O通常是解决网络I / O场景中额外线程需求的良好解决方案。
如果您不想阻止Read,请通过NetworkStream.BeginRead使用异步I / O - 不需要额外的线程,除非您希望回调将接收到的数据的长期工作移交给线程池执行。您可以使用.Net 4,ConcurrentQueue或BlockingCollection中的concurrent collections之一来执行此操作。
如果NetworkStream上没有可用数据,则此代码可能会导致紧密循环。示例here建议您在调用此循环中的Read之前必须检查DataAvailable
。没有这个检查,这段代码似乎有问题。
答案 2 :(得分:0)
将邮件复制到当前正在处理的第二个que并清除传入的que。在复制和清除操作期间,您可能至少需要阻止。
或者,将单个消息处理分配给工作线程,因为它们已被设计。
答案 3 :(得分:0)
您可以考虑使用ThreadPool.QueueUserWorkItem
方法对要在线程池上处理的消息进行排队,而不是使用自定义队列。
优点:
缺点: