在我的申请中提出安全线程的建议?

时间:2010-11-12 21:52:41

标签: c# multithreading

我的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
            }
        }
    }
  1. DissassembleRawMessage负责处理我的缓冲区并在我的客户端消息队列中输入单个消息。没问题。

  2. DeQueueMessages负责处理以前放在DissassembleRawMessage方法中的客户端消息队列中的对象。

  3. 这个问题看起来很明显。收到消息后,我会关闭一个新线程。在对各个消息进行解除和处理的过程中,队列可能会添加更多项目,因为可能已在中间接收到更多数据。这会导致枚举错误,提示我在枚举期间不能修改集合的内容。

    我不想通过在同一个线程内处理队列来阻止我的接收方法,因为它会破坏允许队列建立请求的目的,同时仍然接收数据。如何继续排队对象,同时处理队列中的对象而不会遇到枚举修改异常?

    更新:使消息出列的方法。

    while (client.Queue.Count() > 0)
    {
       byte[] buffer = client.Queue.Dequeue();
       ...
    }
    

4 个答案:

答案 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方法对要在线程池上处理的消息进行排队,而不是使用自定义队列。

优点:

  • 无忧排队,
  • 由于更好​​的工作线程管理而降低了开销,
  • 在整个应用程序中进行线程处理的单一机制。

缺点:

  • 无法控制排队机制,
  • 无法控制线程分配策略。