c#:同步网络通信时出现死锁

本文关键字:死锁 网络通信 同步 | 更新日期: 2023-09-27 18:20:23

我想在c#中开发一个满足以下要求的网络库:

  • 对两个方向的多个请求使用相同的连接
  • 提供一种类似response = Connection.Send(message);的简单包装方法
  • 支持SSL(这就是我不使用SocketAsyncEventArgs的原因)

基本上我有两个线程:

  • First Thread(同步)侦听传入消息并分派所有内容
  • 第二个线程负责请求处理

因此,基本上代码如下(非常简短):

BlockingCollection _receivingQueue;
ConcurrentDictionary <MessageID, ResponseHandlerDelegate> _responseHandlers;
Dictionary <MessageID, Message> _responses;
void ListeningThread()
{
     while(true)
     {
          Message = ReadNetworkStream();
          _receivingQueue.Add(Message);
     }
}
void ProcessingThread()
{
      if(Message == Request)
            Dispatch(message);
      if(Message == ResponseForARequest)
      {
            _responses.Add(Message.ID, Message);
            _responseHandlers[Message.ID](Message);
      }
}

这似乎效果不错。

然而,由于这是一个"易于使用"的库,我想开发上面提到的函数:Response = Connection.Send(Message)

基本上,我认为当新的响应到达时,我可以使用ManuelResetEvent进行通知:

Dictionary <Guid, ManualResetEvents> _resetters;
Message Send(Message)
{
     ManualResetEvent mre = new ManuelResetEvent(false);
     _resetters.Add(Message.ID, mre);
     Connection.Send(Message);
     WaitFor(mre);
     return _responses[Message.ID];
}

MRE是根据注册的ResponseHandlerDelegate 设置的

ResponseArrived(Message)
{
     _responses.Add(Message.ID, Message);
     _resetters[Message.ID].Set();
}

只要没有并发的双向通信,这就可以工作。由于服务器端和客户端都使用相同的代码,它们都在等待MRE的设置。。。

请注意,上面的所有内容都是某种伪代码,因为真正的类太长了。如果您需要特定的代码部分,请告诉我。

那么我的问题是什么呢?

我觉得我制作的东西一团糟。有什么简单的方法可以解决这个问题吗?或者有人能告诉我如何以不同的方式实现这一点吗?

我不想使用WCF,以便更好地控制较低的层,主要是因为我想自己开发它:-)

感谢

c#:同步网络通信时出现死锁

为了在等待同步发送响应时防止阻塞或死锁,您需要一个发送线程PER请求。否则,Send B可以等待A的响应,A可以等待B的响应,两者都不会做任何事情(死锁),因为WaitFor是一个同步锁,它阻止调用Send方法的线程实际接收任何东西。

把它想象成一台状态机,由机器a和B运行你的库:

A --> send to B
B --> send to A
A --> wait for response
B --> wait for response
A --> receive message from B, but still waiting for B's response
B --> receive message from A, but still waiting for A's response

结果:

A --> has message from B but can't stop waiting to process it
B --> has message from A but can't stop waiting to process it

简而言之:您需要2个以上的线程。您需要N个线程,其中N是要同时执行的请求数,即使所有这些线程共享一个连接。使用内置或自制的线程池将有助于重用线程并降低垃圾收集成本。

每个请求旋转一个线程是可以的。。。事实上,这是令人鼓舞的。当单个线程也在等待响应时,不可能用单个线程充分处理多个请求。然而,如果你没有等待回应,这将是一个不同的故事。