C# 0MQ:如何以线程安全的方式使用ZeroMQ?
我读了这本书,无意中发现了以下内容: 您不能在两个服务器之间共享ØMQ套接字 线程。ØMQ套接字不可用 线程安全。从技术上讲这是可能的 要做到这一点,但它需要信号量, 锁或互斥锁。这将使您的 应用程序缓慢且脆弱。唯一的 在一个有点理智的地方 线程之间的共享套接字位于 需要执行的语言绑定 像魔法一样的垃圾收集 插座 后来: 记住:除非在创建套接字的线程中,否则不要使用或关闭套接字 我还了解ZeroMQC# 0MQ:如何以线程安全的方式使用ZeroMQ?,c#,.net,multithreading,thread-safety,zeromq,C#,.net,Multithreading,Thread Safety,Zeromq,我读了这本书,无意中发现了以下内容: 您不能在两个服务器之间共享ØMQ套接字 线程。ØMQ套接字不可用 线程安全。从技术上讲这是可能的 要做到这一点,但它需要信号量, 锁或互斥锁。这将使您的 应用程序缓慢且脆弱。唯一的 在一个有点理智的地方 线程之间的共享套接字位于 需要执行的语言绑定 像魔法一样的垃圾收集 插座 后来: 记住:除非在创建套接字的线程中,否则不要使用或关闭套接字 我还了解ZeroMQ上下文是线程安全的 如果一个类注册另一个类(在.Net中)的事件,则该事件可能会从创建侦听器的线程
上下文是线程安全的
如果一个类注册另一个类(在.Net中)的事件,则该事件可能会从创建侦听器的线程以外的其他线程调用
我认为只有两个选项可以通过ZeroMQ套接字从eventhandler中分派内容:
- 将调用线程的eventhandler与创建ZeroMQ-
套接字的线程同步
- 使用线程安全ZeroMQ-
上下文为eventhandler中的线程创建新的ZeroMQ-套接字
/获取现有的ZeroMQ-套接字
似乎0MQ指南不鼓励第一个,我不认为为每个线程创建一个新的ZeroMq套接字是可行的
我的问题:
从eventhandler中通过0MQ发布消息的正确模式(其含义是什么)是什么
此外,本指南的作者在编写以下内容时是否考虑了.Net的ZeroMQ绑定:
唯一的
在一个有点理智的地方
线程之间的共享套接字位于
需要执行的语言绑定
像魔法一样的垃圾收集
插座
以下是一些示例代码,以强调我的问题:
public class ExampleClass
{
public event EventHandler<ByteEventArgs> SomethinIsCalledFromAnotherThread;
}
public class ByteEventArgs : EventArgs
{
public byte[] BytesToSend;
}
public class Dispatcher
{
ZMQ.Context ctx;
public Dispatcher(ZMQ.Context mqcontext, ExampleClass exampleClassInstance)
{
this.ctx = mqcontext;
exampleClassInstance.SomethinIsCalledFromAnotherThread += new EventHandler<ByteEventArgs>(exampleClass_SomethinIsCalledFromAnotherThread);
}
void exampleClass_SomethinIsCalledFromAnotherThread(object sender, ByteEventArgs e)
{
// this method might be called by a different thread. So I have to get a new socket etc?
using (var socket = ctx.Socket(ZMQ.SocketType.PUSH))
{
// init socket etc..... and finally:
socket.Send(e.BytesToSend);
}
// isn't that too much overhead?
}
}
公共类示例类
{
公共事件事件处理程序SomethinIsCalledFromAnotherThread;
}
公共类ByteEventArgs:EventArgs
{
公共字节[]BytesToSend;
}
公共类调度器
{
ZMQ.Context-ctx;
公共调度程序(ZMQ.Context mqcontext,ExampleClass exampleClassInstance)
{
this.ctx=mqcontext;
exampleClassInstance.SomethinIsCalledFromAnotherThread+=新事件处理程序(exampleClass\u SomethinIsCalledFromAnotherThread);
}
void exampleClass_SomethinIsCalledFromAnotherThread(对象发送器,ByteEventTargets e)
{
//这个方法可能被另一个线程调用。所以我必须得到一个新的套接字等等?
使用(var socket=ctx.socket(ZMQ.SocketType.PUSH))
{
//初始化套接字等…最后:
socket.Send(例如BytesToSend);
}
//这不是太多开销吗?
}
}
您可以创建大量的0MQ套接字,当然可以创建尽可能多的线程。如果在一个线程中创建套接字,并在另一个线程中使用它,则必须在两个操作之间执行完整的内存屏障。其他任何东西都会导致libzmq中奇怪的随机故障,因为套接字对象不是线程安全的
有几种传统模式,但我不知道它们是如何具体映射到.NET的:
在使用套接字的线程中创建套接字,句号。在紧密绑定到一个进程的线程之间共享上下文,并在不紧密绑定的线程中创建单独的内容。在高级C API(czmq)中,这些线程称为连接线程和分离线程
在父线程中创建套接字,并在线程创建时将其传递给连接的线程。线程创建调用将执行一个完整的内存屏障。从那时起,只在子线程中使用套接字。“使用”指接收、发送、设置sockopt、设置sockopt和关闭
在一个线程中创建一个套接字,在另一个线程中使用,在每次使用之间执行自己的完整内存屏障。这是非常微妙的,如果你不知道什么是“完整的记忆障碍”,你不应该这样做
别忘了看看inproc的交通工具。使用inproc://sockets进行线程间通信可能很有用,并且有一个线程打开套接字与其他进程/服务器通信
您仍然需要每个线程至少一个套接字,但inproc套接字根本不涉及IP网络层。在.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;
sendQueue=newblockingcollection(new ConcurrentQueue());
//并发队列可以安全地接受来自多个线程/处理程序的请求
MyHandler+=(MyStuff stuffToSend)=>sendQueue.Add(stuffToSend);
//启动单线程数据发送循环
Task.Factory.StartNew(()=>{
使用(var socket=context.socket()){
MyStuff Stuff Tosend;
//在调用CompleteAdding之前,此可枚举项将被阻塞
foreach(sendQueue.GetConsumingEnumerable()中的var stuff)
Send(stuff.Serialize());
}
});
//完成后中断发送循环
OnMyAppExit+=sendQueue.completedadding;
在.NET中,有许多操作涉及线程池(例如任务),其中一个操作在下一个可用线程上运行;您不知道它将是哪个线程。有几个地方隐式使用了完整的内存屏障,但我怀疑测试这段代码会非常棘手。我今天早上刚读了问题中的引语,想看看是否有人解决了这个问题。背景如何?建议在线程之间共享还是为不同的线程创建上下文?@Pieter,Rest in Peace我最近使用了这种方法,我认为这是一种惯用的方法来处理.Net4中的“仅从一个线程发送”情况。然而,它并不能解决这些问题