0MQ:如何以线程安全的方式使用ZeroMQ?

时间:2011-04-30 12:59:00

标签: c# .net multithreading thread-safety zeromq

我读了ZeroMq guide,我偶然发现了以下内容:

  

你之间不能共享ØMQ套接字   线程。 ØMQ插座不是   线程安全的。从技术上讲,这是可能的   要做到这一点,但它需要信号量,   锁或互斥锁。这将使你的   应用缓慢而脆弱。唯一的   远离它的地方   线程之间的共享套接字在   需要做的语言绑定   像垃圾收集一样神奇   套接字。

以及后来:

  

请记住:除了创建它们的线程外,不要使用或关闭套接字。

我也明白ZeroMQ Context是线程安全的。

如果一个类在.Net中注册另一个类的事件,则可以从与创建监听器的线程不同的线程调用此事件。

我认为只有两个选项可以从事件处理程序中通过ZeroMQ-Sockets调度内容:

  • 将eventhandler-invoking-thread同步到ZeroMQ的线程 - {/ 1}}在
  • 中创建
  • 使用线程安全的ZeroMQ创建一个新的ZeroMQ - Socket /获取现有的ZeroMQ - Socket for the threadhand - Socket

似乎0MQ-Guide不鼓励第一个,我不认为为每个线程创建一个新的ZeroMq-Socket是高性能/可行的方式。

我的问题
在事件处理程序中通过0MQ发布消息的正确模式(它的方式是什么)是什么?

此外,该指南的作者在撰写时还考虑过.Net的ZeroMQ-Binding:

  

唯一的   远离它的地方   线程之间的共享套接字在   需要做的语言绑定   像垃圾收集一样神奇   插座。 ?

以下是一些强调我的问题/问题的示例代码:

Context

3 个答案:

答案 0 :(得分:25)

你可以创建许多0MQ套接字,当然和你拥有的线程一样多。如果在一个线程中创建套接字,并在另一个线程中使用它,则必须在两个操作之间执行完整的内存屏障。其他任何东西都会导致libzmq出现奇怪的随机故障,因为套接字对象不是线程安全的。

有一些传统模式,虽然我不知道这些模式是如何专门针对.NET的:

  1. 在使用它们的线程中创建套接字,句点。在紧密绑定到一个进程的线程之间共享上下文,并在未严格绑定的线程中创建单独的内容。在高级C API(czmq)中,这些被称为附加和分离线程。
  2. 在父线程中创建套接字,并在创建线程时将 传递给附加线程。线程创建调用将执行完整的内存屏障。从那时起,在子线程中使用socket only 。 “use”表示recv,send,setsockopt,getsockopt和close。
  3. 在一个线程中创建一个套接字,并在另一个线程中使用,在每次使用之间执行自己的完整内存屏障。这非常微妙,如果你不知道“完全记忆障碍”是什么,你不应该这样做。

答案 1 :(得分:7)

在.net framework v4及更高版本中,您可以使用并发集合来解决此问题。即生产者 - 消费者模式。多个线程(处理程序)可以将数据发送到线程安全队列,只有单个线程使用队列中的数据并使用套接字发送它。

这是一个想法:

sendQueue = new BlockingCollection<MyStuff>(new ConcurrentQueue<MyStuff>());
// concurrent queue can accept from multiple threads/handlers safely
MyHandler += (MyStuff stuffToSend) => sendQueue.Add(stuffToSend);

// start single-threaded data send loop
Task.Factory.StartNew(() => {
    using(var socket = context.Socket()) {
        MyStuff stuffToSend;
        // this enumerable will be blocking until CompleteAdding is called
        foreach(var stuff in sendQueue.GetConsumingEnumerable())
            socket.Send(stuff.Serialize());
    }
});

// break out of the send loop when done
OnMyAppExit += sendQueue.CompleteAdding;

答案 2 :(得分:2)

不要忘记查看inproc传输。使用inproc://套接字进行线程间通信可能很有用,并且有一个线程可以打开套接字与其他进程/服务器通信。

每个线程仍需要至少一个套接字,但inproc版本根本不需要IP网络层。