ZeroMQ 教程 002 : 高级技巧 (11)

而还有一种更简洁的写法(这种简洁的写在其实是有潜在的漏洞的, 详情请参见<Unix环境高级编程>(<Advanced Programming in the UNIX Environment>) 第十章(chapter 10. Signals) )

定义一个全局变量 s_interrupted

定义一个信号处理函数, 该信号处理函数在接收到诸如SIGINT之类的信号时, 置s_interrupted为1

在业务处理逻辑中, 判断全局变量s_interrupted的值, 若该值为1, 则进入退出流程

大致如下:

s_catch_signals(); // 注册事件回调 client = zmq_socket(...); while(!s_interrupted) // 时刻检查 s_interrupted 的值 { char * message = s_recv(client); if(!message) { break; // 接收消息异常时也退出 } // 处理业务逻辑 } zmq_close(close); 避免内存泄漏

服务端应用程序最蛋疼的问题就是内存泄漏了, 这个问题已经困扰了C/C++程序员二三十年了, ZMQ在这里建议你使用工具去检测你的代码是否有内存泄漏的风险. 这里建议你使用的工具是: valgrind

默认情况下, ZMQ本身会导致valgrind报一大堆的警告, 首先先屏蔽掉这些警告. 在你的工程目录下新建一个文件名为 vg.supp, 写入下面的内容

{ <socketcall_sendto> Memcheck:Param socketcall.sendto(msg) fun:send ... } { <socketcall_sendto> Memcheck:Param socketcall.send(msg) fun:send }

然后记得妥善处理掉诸如SIGINT与SIGTERM这样的Signal. 否则valgrind会认为不正确的退出程序会有内存泄漏风险. 最后, 在编译你的程序时, 加上 -DDEBUG 选项. 然后如下运行valgrind

valgrind --tool=memcheck --leak-check=full --suppression=vg.supp <你的程序>

如果你的代码写的没有什么问题, 会得到下面这样的赞赏

==30536== ERROR SUMMARY: 0 errors from 0 contexts... 在多线程环境使用 ZMQ

啊, 多线程, 给大家讲一个笑话, 小明有一个问题, 然后小明决定使用多线程编程解决这个问题. 最后小明问题两个了有.

传统的多线程编程中, 或多或少都会掺入同步手段. 而这此同步手段一般都是程序员的噩梦, 信号量, 锁. ZMQ则告诫广大程序员: 不要使用信号量, 也不要使用锁, 不要使用除了 zmq inproc之外的任何手段进行线程间的数据交互.

ZMQ在多线程上的哲学是这样的:

多线程应该以并行优势提高程序运行效率

避免线程同步. 如果你的多线程程序需要大量的代码来完成线程同步, 那么一定是你的程序设计有问题.

如果非得同步, 那么不要使用锁或信号量. 而使用 zmq inproc socket 来在线程间传递信息

良好的多线程程序设计, 应当很容易的将其改造成多进程服务, 更进一步, 应该很容易的拆分进程以部署在不同的机器结点上.

总的来说, 以多进程的设计思路去设计多线程程序, 核心哲学是避免线程同步.

更细节的, 在进行多线程编程时, 你应当遵循以下的几个点:

将数据进行独立拆分, 每个线程只访问自己的私有数据, 避免多线程共享数据. 除了一个例外: zmq的context实例

避免使用传统的线程同步手段: 信号量, 临界区, 锁. 上面已经强调过了, 不要使用这些手段.

在程序一开始处, 未创建线程时, 创建context实例, 随后将这个context实例共享给所有线程

如果父线程需要创建数据实例, 那么开attached线程创建程序中要使用的数据实例. 然后通过inproc pair socket将数据实例回传. 父线程bind, 子线程connect

如果父线程需要并行的子线程来处理业务. 那么开detached线程来跑业务, 并在各子线程中为各个子线程创建自己独享的context. 父子线程使用tcp socket进行通信. 这样你的程序就会很容易的扩展成多进程服务, 而不需要改动过多的代码.

线程间的数据交互一律使用zmq socket传递消息.

不要在线程间传递socket句柄. zmq socket实例不是线程安全的. 从本质上讲在线程间传递socket句柄是可行的, 但这要建立在经验丰富的基础上, 否则只会让事情更大条. 在线程间传递socket实例一般情况下只发生在zmq库的其它编程语言的binding库上, 一般也是用于带GC的语言去处理自动对象回收. 这种技巧不应该出现在zmq的使用者身上.

如果你程序要用到多个掮客, 比如, 多个线程都拥有自己独立的掮客, 一个常见的错误就是: 在A线程里创建掮客的左右两端socket, 然后将socket传递给B线程里的掮客. 这违反了上面的规则: 不要在线程间传递socket. 这种错误很难发觉, 并且出错是随机的, 出现问题后很难排查.

ZMQ对线程库是没有侵入性的, ZMQ没有内置线程库, 也没有使用其它的线程实例. 使用ZMQ写多线程应用程序, 多线程接口就是操作系统操作的线程接口. 所以它对线程相关的检查工具是友好的: 比如Intel的Thread Checker. 这种设计的坏处就是你写的程序在线程接口这一层的可移植性需要你自己去保证. 或者你需要使用其它第三方的可移植线程库.

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypgfs.html