ZeroMQ:04---ZeroMQ上下文详解

ZeroMQ应用程序始于上下文创建,使用zmq_ctx_new()创建,zmq_ctx_term()销毁。通常一个进程内可有多个独立上下文。zmq_ctx_term()会等待所有套接字关闭或linger超时。设置ZMQ_LINGER为0可避免关闭时的阻塞。zmq_ctx_set()用于设置上下文选项,如I/O线程数。zmq_ctx_get()用于获取这些选项。zmq_ctx_shutdown()关闭上下文但不释放资源,后续需调用zmq_ctx_term()。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、上下文的创建与退出

  • ZeroMQ应用程序总是从创建一个上下文开始,然后使用它来创建套接字。
  • 上下文是指:在单个进程中所有套接字的容器,并充当inproc套接字的传输工具,这是在一个进程中连接线程的最快办法。
  • 单个进程中可以有多个上下文,不会出错,它们彼此之间都是独立的ZeroMQ实例,因此你需要自己做好处理。
  • 但是一般使用规则为:在你的主代码开始处执行一个zmq_ctx_new()、在代码最后执行一个zmq_ctx_destroy()。
  • 父子进程之间的上下文:
    • 父子进程之间一般都要拥有自己的上下文。
    • 如果父进程调用zmq_ctx_new()创建了上下文,fork()之后子进程会获得自己的上下文。

1. zmq_ctx_new()

 void *zmq_ctx_new ();
  • API参考手册:http://api.zeromq.org/master:zmq-ctx-new
  • 功能:该函数创建一个新ØMQ上下文。
  • 该函数替换了已过时的zmq_init()函数。
  • 返回值:
    • 如果成功,函数向新创建的上下文返回不透明句柄。
    • 否则,它将返回NULL。
  • errno: 没有为此函数定义错误值。
  • 线程安全: ØMQ上下文是线程安全的,可以根据需要在任意多个应用程序线程之间共享,而调用方无需进行任何其他锁定。

2. zmq_ctx_term()

int zmq_ctx_term(void *context);
  • API参考手册:http://api.zeromq.org/master:zmq-ctx-term
  • 功能:该函数终止一个ØMQ上下文。
  • 参数:上下文句柄(配对的zmq_ctx_new()函数的返回值)。
  • 返回值
    • 如果成功,函数返回零。
    • 否则,它将返回-1并将errno设置为以下定义的值之一:
      • EFAULT:提供的上下文无效。
      • EINTR:终止被信号中断。如果需要,可以重新启动它。
  • 上下文终止按以下步骤执行
    • 当前在上下文中打开的套接字上正在进行的任何阻塞操作都应立即返回,并返回错误代码ETERM。除zmq_close()之外,在上下文中打开的套接字上的任何进一步操作均将失败,并显示错误代码ETERM。
    • 在中断所有阻塞调用之后,zmq_ctx_term()将阻塞直到满足以下条件
      • 在上下文中打开的所有套接字都已使用zmq_close()关闭。
      • 对于上下文中的每个套接字,应用程序使用zmq_send()发送的所有消息都已物理传输到网络对等方,或者使用套接字ZMQ_LINGER选项设置的套接字停留时间已到期。
  • 附加
    • 有关套接字延迟行为的更多详细信息,请参考zmq_setsockopt()中的ZMQ_LINGER选项。
    • 该函数替换了不推荐使用的zmq_term()和zmq_ctx_destroy()函数。

二、执行彻底的退出

  • 让程序的彻底退出一般有两个原因:
    • ①对于C/C++程序来说,在程序终止之后一般需要对申请的资源进行释放,否则就会造成泄漏。
    • 如果任何套接字保持打开,那么zmq_ctx_term()函数将一直挂起;如果套接字已经全部关闭,但是有悬而未决的连接或发送,那么zmq_ctx_term()默认也将等待下去。除非你在关闭这些套接字之前,将它们的LINGER(延期)设置为0。

1. 建议如下

  • 一个ZeroMQ对象包括消息、套接字、上下文
  • 所以,在简单的程序中,一般建议的规则为
    • 处理完消息的那一刻,总是调用zmq_msg_close()关闭它。
    • 如果你打开和关闭了很多套接字,那么你的程序可能设计的不太合理,可以考虑重新设计程序。
    • 当你退出程序时,关闭你的套接字,然后调用zmq_ctx_term()销毁上下文。
  • 至少C语言是这样的。在自动销毁对象的语言中,当你离开作用域时,套接字和上下文将销毁。
  • 如果你使用异常,必须在一个类似“final”块的东西中执行清理工作。

2. 多线程中的处理

  • 如果是多线程,那么情况可能会复杂一些,在后面的章节中我们会研究多线程,此处只是简单地一些介绍。
  • 一般的规则为
  • 首先,不要在多个线程中使用同一个套接字。
  • 接着,你需要关闭每个有持续请求的套接字,正确的方法是先设置一个低的LINGER值(1秒),然后关闭套接字。
  • 最后销毁上下文,这将导致任何连接到线程(即,它们共享相同的上下文)的阻塞的接收或调查或发送都返回一个错误。捕获该错误,然后在该线程中设置LINGER,关闭套接字并退出。
  • 不要多次销毁同样的上下文
  • 在主线程中的zmq_ctx_term()调用将会保持阻塞,直到它直到的所有套接字都已安全地关闭为止

三、设置上下文选项(zmq_ctx_set)

int zmq_ctx_se(void *context, int option_name, int option_value);
选项值 含义
ZMQ_BLOCKY(修复阻止行为)默认情况下,在zmq_ctx_term调用上,上下文将永远阻塞。这种行为背后的假设是,突然终止将导致消息丢失。大多数实际应用程序使用某种形式的握手来确保应用程序接收终止消息,然后在所有套接字上将ZMQ_LINGER设置为0来终止上下文。此设置是获得相同结果的一种更简单的方法。当ZMQ_BLOCKY设置为false时,所有新套接字的延迟超时都为零。在调用zmq_ctx_term之前,仍然必须关闭所有套接字 默认值:true
ZMQ_IO_THREADS(设置I / O线程数)ZMQ_IO_THREADS参数指定的大小ØMQ线程池来处理I / O操作。如果应用程序仅使用inproc传输来进行消息传递,则可以将其设置为零,否则至少设置为一个。此选项仅适用于在上下文上创建任何套接字之前。 默认值:1
ZMQ_THREAD_SCHED_POLICY(设置I / O线程的调度策略)ZMQ_THREAD_SCHED_POLICY参数为内部上下文的线程池设置调度策略。此选项在windows上不可用。该选项支持的值可以在sched.h文件或http://man7.org/linux/manpages/man2/sched_setscheduler.2.html中找到。此选项仅适用于在上下文上创建任何套接字之前。默认值:-1
ZMQ_THREAD_PRIORITY(设置I / O线程的调度优先级)ZMQ_THREAD_PRIORITY参数为内部上下文的线程池设置调度优先级。此选项在windows上不可用。此选项的支持值取决于所选的调度策略。在Linux上,当调度器策略为SCHED_OTHER、SCHED_IDLE或SCHED_BATCH时,OS调度器不会使用线程优先级,而是使用线程“nice值”;在这种情况下,系统调用“nice”将用于将nice值设置为-20(最大优先级),而不是调整线程优先级(对于那些调度策略,线程优先级必须为零)。详细信息可以在sched.h文件或http://man7.org/linux/manpages/man2/sched_setscheduler.2.html中找到。此选项仅适用于在上下文上创建任何套接字之前。默认值:-1
ZMQ_THREAD_AFFINITY_CPU_ADD(将CPU添加到I / O线程的相似性列表)ZMQ_THREAD_AFFINITY_CPU_ADD参数将特定的CPU添加到内部上下文的线程池的关联列表中。此选项仅在Linux上支持。此选项仅适用于在上下文上创建任何套接字之前。默认的关联列表是空的,这意味着不会在内部上下文的线程上设置显式的cpu关联。默认值:-1
ZMQ_THREAD_AFFINITY_CPU_REMOVE(从CPU移除对I / O线程的相似性列表)ZMQ_THREAD_AFFINITY_CPU_REMOVE参数将一个特定的CPU移除到内部上下文的线程池的关联列表中。此选项仅在Linux上支持。此选项仅适用于在上下文上创建任何套接字之前。默认的关联列表是空的,这意味着不会在内部上下文的线程上设置显式的cpu关联。默认值:-1
ZMQ_THREAD_NAME_PREFIX(设置I / O线程的名称前缀)ZMQ_THREAD_NAME_PREFIX参数为为内部上下文的线程池创建的每个线程设置一个数字前缀。此选项仅在Linux上支持。该选项有助于通过“top -H”或“gdb”完成调试;如果系统上的多个进程使用ZeroMQ,那么通过这个上下文选项提供一个特定于应用程序的前缀来区分属于不同进程的ZeroMQ后台线程是很有用的。此选项仅适用于在上下文上创建任何套接字之前。默认值:-1
ZMQ_MAX_MSGSZ(设置最大邮件大小)ZMQ_MAX_MSGSZ参数设置上下文中发送的消息允许的最大大小。您可以使用ZMQ_MAX_MSGSZ选项使用zmq_ctx_get查询允许的最大值。默认值:INT_MAX;最大值:INT_MAX
ZMQ_ZERO_COPY_RCV(指定消息解码策略)MQ_ZERO_COPY_RCV参数指定消息解码器在接收消息时是否应该使用零拷贝策略。在某些情况下,零拷贝策略会导致内存使用量增加。此选项允许您使用较旧的复制策略。您可以使用ZMQ_ZERO_COPY_RECV选项使用zmq_ctx_get查询此选项的值。注意:处于草案状态,在稳定版本中还不可用。默认值:1
ZMQ_MAX_SOCKETS(设置最大套接字数)ZMQ_MAX_SOCKETS参数设置上下文上允许的最大套接字数量。可以使用ZMQ_SOCKET_LIMIT选项使用zmq_ctx_get查询允许的最大值 默认值:1024
ZMQ_IPV6(设置IPv6选项)ZMQ_IPV6参数为从此上下文中创建的所有套接字设置IPv6值。值1表示启用IPv6,而0表示套接字只使用IPv4。当启用IPv6时,套接字将连接到IPv4和IPv6主机,或接受来自IPv4和IPv6主机的连接。默认值:0
  • 返回值:
    • 成功:
    • 失败:返回-1,并将errno设置为以下定义值之一:
      • EINVAL:请求的选项option_name未知。
  • 演示案例:设置套接字数量限制。
void *context = zmq_ctx_new ();
zmq_ctx_set (context, ZMQ_MAX_SOCKETS, 256);
int max_sockets = zmq_ctx_get (context, ZMQ_MAX_SOCKETS);
assert (max_sockets == 256);

1. I/O线程(ZMQ_IO_THREADS选项)

  • 默认情况下,ØMQ在1个后台线程中执行I/O。当你创建一个新的上下文时,后台的I/O线程开始工作。
  • 除最极端的应用程序外,1个I/O线程(用于所有套接字)足以供所有应用程序使用
  • 如果想要提高I/O线程的数量,可在创建任何套接字前使用zmq_ctx_set()函数设置I/O线程的数量。例如:ZMQ_IO_THREADS选项指定的大小ØMQ线程池来处理I/O操作。此选项仅适用于在上下文上创建任何套接字之前。
int io_threads = 4;
void *context = zmq_ctx_new();
 
//设置I/O线程数量
zmq_ctx_set(context, ZMQ_IO_THREADS, io_threads);
assert(zmq_ctx_get(context, ZMQ_IO_THREADS) == io_threads);
  • 如果应用程序仅使用inproc来进行线程间通信(即一个不执行外部套接字I/O的多线程的应用程序),则可以把I/O线程数设置为0

四、获取上下文选项(zmq_ctx_get())

int zmq_ctx_get(void *context, int option_name);
选项值 含义
ZMQ_IO_THREADS(获取I/O线程数)返回当前上下文背景下的线程池的大小
ZMQ_MAX_SOCKETS(获取最大套接字数)返回当前上下文背景下的套接字的最大数量
ZMQ_MAX_MSGSZ(获取最大消息大小)返回当前上下文背景下的消息的最大大小。默认值为INT_MAX
ZMQ_ZERO_COPY_RCV(获取消息解码策略)接收消息时返回参数是否消息解码器使用一个零复制策略。默认值为1。(备注:此选项在DRAFT状态下,尚不能在稳定版本中使用)
ZMQ_SOCKET_LIMIT(获取最大数量的可配置套接字)返回可设置的socket套接字的最大值
ZMQ_IPV6(获取IPv6选项)返回上下文中IPv6选项
ZMQ_BLOCKY(获取阻塞设置)如果返回1:如果上下文在终止时将阻塞,则返回1;如果返回0:如果在所有新上下文将ZMQ_BLOCKY设置为false而禁用了“永远阻塞上下文终止”策略,则返回0
ZMQ_THREAD_SCHED_POLICY(获取I/O线程的调度策略)返回上下文的线程池调度策略
ZMQ_THREAD_NAME_PREFIX(获取I/O线程的名称前缀)获取为内部上下文的线程池创建的每个线程的数字前缀
ZMQ_MSG_T_SIZE(在运行时获取zmq_msg_t的大小)返回zmq_msg_t结构在运行时的大小,这是在include/zmq.h公共头中定义的。这对于不能简单地执行sizeof()的FFI绑定很有用
  • 返回值:
    • 成功:返回0或更大的值。
    • 否则,返回-1,并将errno设置为以下定义值之一:
      • EINVAL:请求的选项option_name未知。
  • 演示案例:设置套接字数量限制并获取:
void *context = zmq_ctx_new ();
zmq_ctx_set (context, ZMQ_MAX_SOCKETS, 256);
int max_sockets = zmq_ctx_get (context, ZMQ_MAX_SOCKETS); 
assert (max_sockets == 256);
  • 演示案例:关闭上下文死锁技巧:
zmq_ctx_set (ctx, ZMQ_BLOCKY, false);

五、关闭上下文(zmq_ctx_shutdown())

int zmq_ctx_shutdown(void *context);
  • API参考手册:http://api.zeromq.org/master:zmq-ctx-shutdown
  • 功能:关闭上下文。
  • 上下文关闭将导致在上下文中打开的套接字上当前正在进行的任何阻塞操作立即返回,错误代码为ETERM。除zmq_close()之外,在上下文中打开的套接字上的任何进一步操作均将失败,并显示错误代码ETERM。
  • 与zmq_ctx_term()函数关系:zmq_ctx_shutdown()函数的使用是可选的,其只关闭上下文但是不释放上下文相关的资源。因此在调用该函数之后一般还需要调用zmq_ctx_term()来释放ZeroMQ所分配的资源该。
  • 参数:要关闭的上下文。
  • 返回值:
    • 成功:返回0。
    • 失败:返回-1并将errno设置为如下值之一:
      • EFAULT:提供的上下文无效。

六、已过时的函数

ZeroMQ—指导 由iMatix公司的首席执行官Pieter Hintjens 编写。感谢Bill Desmarais, Brian Dorsey, CAF, Daniel Lin, Eric Desgranges, Gonzalo Diethelm, Guido Goldstein, Hunter Ford, Kamil Shakirov, Martin Sustrik, Mike Castleman, Naveen Chawla, Nicola Peduzzi, Oliver Smith, Olivier Chamoux, Peter Alexander, Pierre Rouleau, Randy Dryburgh, John Unwin, Alex Thomas, rofl0r, Mihail Minkov, Jeremy Avnet, Michael Compton, and Zed Shaw的贡献,也感谢Stathis Sideris,因为Ditaa.。 请对所有的意见和勘误表进行问题跟踪。这个版本覆盖了0MQ2.0的版本,发表于周二2010年11月9日,9时32分19秒。 第一章——基础的东西 修理(fixing)这个世界 怎么解释0MQ?有些人会说它的所有美好的事情。它是类固醇(steroids)上的套接字。它像有路由的邮箱。它很快。别人想分享它的启蒙,当这一切变得越来越明显,人们开始顿悟了。事情变得更简单。再也不复杂了。它打开了人们的思维。其他人喜欢通过做比较的方式来解释。它更小,更简单,但是看起来仍然很熟悉。就我个人而言,我希望回忆起我们为什么要开发0MQ,因为,这是读者们也很想知道的问题。 编程是装扮成艺术的科学,因为我们中的大多数不了解软件的物理过程。如果学过这方面的知识的话,也学的很少。软件的物理过程不是算法,数据结构,语言和抽象。这些只是我们研发,使用,然后扔掉的工具。软件真正的物理过程实际上是人们的思维过程。 ……
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值