如何在GTK/QT/Clutter应用程序中使用ZeroMQ?

hlo*_*dal 6 gtk zeromq

gtk应用程序中,所有执行都在gtk_main函数内部进行.和其它图形帧作品也有类似的事件循环状app.execQTclutter_main杂波.但是,ZeroMQ基于这样的假设:它有一个while (1) ...插入的循环(例如,参见这里的示例).

你如何结合这两种执行策略?

我目前想在用C编写的杂乱应用程序中使用zeromq,所以我当然希望直接回答这个问题,但请为其他变体添加答案.

zby*_*zek 12

组合zmq和gtk或clutter的正确方法是将zmq队列的文件描述符连接到主事件循环.可以使用检索fd

int fd;
size_t sizeof_fd = sizeof(fd);
if(zmq_getsockopt(socket, ZMQ_FD, &fd, &sizeof_fd))
      perror("retrieving zmq fd");
Run Code Online (Sandbox Code Playgroud)

将它连接到主循环是使用io_add_watch的问题:

GIOChannel* channel = g_io_channel_unix_new(fd);    
g_io_add_watch(channel, G_IO_IN|G_IO_ERR|G_IO_HUP, callback_func, NULL);
Run Code Online (Sandbox Code Playgroud)

在回调函数中,有必要在读取之前首先检查是否确实要读取内容.否则,该函数可能会阻止等待IO.

gboolean callback_func(GIOChannel *source, GIOCondition condition,gpointer data)
{
    uint32_t status;
    size_t sizeof_status = sizeof(status);   

    while (1){
         if (zmq_getsockopt(socket, ZMQ_EVENTS, &status, &sizeof_status)) {
             perror("retrieving event status");
             return 0; // this just removes the callback, but probably
                       // different error handling should be implemented
         }
         if (status & ZMQ_POLLIN == 0) {
             break;
         }

         // retrieve one message here
    }
    return 1; // keep the callback active
}
Run Code Online (Sandbox Code Playgroud)

请注意:这实际上没有经过测试,我从Python + Clutter进行了翻译,这是我使用的,但我很确定它会起作用.作为参考,下面是完整的Python + Clutter代码,它实际上是有效的.

import sys
from gi.repository import Clutter, GObject
import zmq

def Stage():
    "A Stage with a red spinning rectangle"
    stage = Clutter.Stage()

    stage.set_size(400, 400)
    rect = Clutter.Rectangle()
    color = Clutter.Color()
    color.from_string('red')
    rect.set_color(color)
    rect.set_size(100, 100)
    rect.set_position(150, 150)

    timeline = Clutter.Timeline.new(3000)
    timeline.set_loop(True)

    alpha = Clutter.Alpha.new_full(timeline, Clutter.AnimationMode.EASE_IN_OUT_SINE)
    rotate_behaviour = Clutter.BehaviourRotate.new(
        alpha, 
        Clutter.RotateAxis.Z_AXIS,
        Clutter.RotateDirection.CW,
        0.0, 359.0)
    rotate_behaviour.apply(rect)
    timeline.start()
    stage.add_actor(rect)

    stage.show_all()
    stage.connect('destroy', lambda stage: Clutter.main_quit())
    return stage, rotate_behaviour

def Socket(address):
    ctx = zmq.Context()
    sock = ctx.socket(zmq.SUB)
    sock.setsockopt(zmq.SUBSCRIBE, "")
    sock.connect(address)
    return sock

def zmq_callback(queue, condition, sock):
    print 'zmq_callback', queue, condition, sock

    while sock.getsockopt(zmq.EVENTS) & zmq.POLLIN:
        observed = sock.recv()
        print observed

    return True

def main():
    res, args = Clutter.init(sys.argv)
    if res != Clutter.InitError.SUCCESS:
        return 1

    stage, rotate_behaviour = Stage()

    sock = Socket(sys.argv[2])
    zmq_fd = sock.getsockopt(zmq.FD)
    GObject.io_add_watch(zmq_fd,
                         GObject.IO_IN|GObject.IO_ERR|GObject.IO_HUP,
                         zmq_callback, sock)

    return Clutter.main()

if __name__ == '__main__':
    sys.exit(main())
Run Code Online (Sandbox Code Playgroud)


pto*_*ato 6

听起来ZeroMQ代码希望尽可能经常地一遍又一遍地执行.最简单的方法是将ZeroMQ代码放入空闲函数或超时函数,并使用非阻塞版本的函数(如果存在).

对于Clutter,你会使用clutter_threads_add_idle()clutter_threads_add_timeout().对于GTK,你会使用g_idle_add()g_timeout_add().

更难但可能更好的方法是使用ZeroMQ代码创建一个单独的线程g_thread_create(),并且只使用while(1)带有阻塞函数的构造.如果你这样做,你还必须找到一些方法让线程相互通信 - GLib的互斥和异步队列通常都可以.

  • 不会在空闲计时器中轮询zeromq导致100%的CPU使用率? (2认同)
  • 在一个空闲函数中,可能是的,至少在GTK主循环没有做任何事情时.在超时功能中,没有. (2认同)