1

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

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

基本上我有两个线程:

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

所以基本上代码如下(非常缩短):

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 来更好地控制较低层,并且 - 主要是因为我想自己开发它 :-)

谢谢

4

1 回答 1

1

为了防止在等待同步发送响应时出现阻塞或死锁,您需要一个发送线程 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 是您想要并发执行的请求数,即使所有这些线程共享一个连接。使用内置或自制的线程池将帮助您重用线程并降低垃圾收集成本。

每个请求启动一个线程是可以的……事实上,这是鼓励的。当单个线程也等待响应时,不可能用单个线程充分处理多个请求。但是,如果您不等待回复,这将是另一回事。

于 2013-06-28T18:23:49.807 回答