七、GLib主要的事件循环(The Main Event Loop)

1 GMainLoop、GMainContext、GSource

1.1 三者之间关系

  • GMainLoop -> GMainContext -> {GSource1, GSource2,GSource3… }
  • 每个GMainLoop都包含一个GMainContext成员,而这个GMainContext成员可以装各种各样的GSource,GSource则是具体的各种Event处理逻辑了。在这里,可以把GMainContext理解为GSource的容器,但是它的用处不只是装GSouce。
  • 创建GMainLoop使用函数g_main_loop_new,他的第一个参数就是需要关联的GMainContext,如果这个值为空,程序会分配一个默认的Context给GMainLoop,把GSource加到GMainContext使用g_source_attach。
  • g_main_loop_new创建一个main loop对象,一个main loop对象只能被一个线程使用,但一个线程可以有多个main loop对象。
  • GLib内部实现了几种类型的事件源,分别是文件描述符(文件、管道和socket)、超时idle事件自定义事件

1.2 GMainContext

  为了让多组独立事件源能够在不同的线程中被处理,每个事件源都会关联一个GMainContext。一个线程只能运行一个GMainContext,都是在其他线程中能够对事件源进行添加和删除操作。

GMainConText涉及的内容比较多,在后面章节相似介绍。

2 事件源

注意:在事件源回调函数function中,如果回调函数返回值是FALSE,该事件源将被删除;反则该事件源会在没有更高优先级事件时,再次运行。

2.1 超时事件源

guint    g_timeout_add              (guint           interval,
                                     GSourceFunc     function,
                                     gpointer        data);

2.2 空闲函数事件源

添加一个在默认主循环中没有高优先级事件待决时调用的函数。该函数具有默认的空闲优先级G_PRIORITY_DEFAULT_IDLE。如果函数返回FALSE,它将自动从事件源列表中删除,并且不会再次调用。

关于如何处理返回值和数据的内存管理,请参阅[内存管理][mainloop-memory-management]。

这在内部使用g_idle_source_new()创建了一个主循环源,并使用g_source_attach()将其附加到全局GMainContext,因此无论哪个线程运行该主上下文,都会调用回调函数。如果需要更好的控制或使用自定义的主要上下文,可以手动执行这些步骤。

该函数不能直接用于语言绑定。

这个方法的实现由语言绑定中的g_idle_add_full()提供

guint    g_idle_add                 (GSourceFunc     function,
                                     gpointer        data);

2.3 描述字事件源

#include <glib.h>
#include <gio/gio.h> /* gio channel */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


gboolean 
client_recv (GIOChannel   *source, 
             GIOCondition  condition,
			       gpointer      data){
  gchar buff[128];
  gint recv_byte = 128;
  gint recv_sizing;

  gint clienct_insock = g_io_channel_unix_get_fd(source);
  recv_sizing = recv(clienct_insock, buff, recv_byte, 0);
  if(recv_sizing < 0 || recv_sizing == 0){
    g_print("connection lost or error while recv(); [just guess] number : %d\n", recv_sizing);
    close(clienct_insock);
    return FALSE;
  }

  buff[recv_sizing] = '\0';
  g_print("data: %s\n", buff);
  return TRUE;
}


gboolean
deal(GIOChannel *in, GIOCondition condition, gpointer data){
  
  struct sockaddr_in income;
  gint insock = g_io_channel_unix_get_fd(in);
  socklen_t income_len = sizeof(income);
  /*接受请求*/
  gint newsock = accept(insock, (struct sockaddr*)&income, &income_len);
  if(newsock == -1){
    g_print("failure on newsock\n");
  } 

  g_print("Linked successfully\n");

  /*得到客户端描述字*/
  GIOChannel *client_in = g_io_channel_unix_new(newsock);
  /*添加到主循环事件*/
  g_io_add_watch(client_in,
                 G_IO_IN | G_IO_OUT | G_IO_HUP,
                 (GIOFunc)client_recv,
                 NULL);
  return TRUE;
}

int
main(int argc, char *argv[]){
  GIOChannel *in;

  /*打开一个网络通讯端口,如果成功的话就像open()一样返回一个文件描述符*/
  gint rsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(rsock < 0){
    g_printerr("socket\n");
    return 1;
  }

  /*绑定端口号*/
  struct sockaddr_in my;
  my.sin_family = AF_INET;/* 套接口地址结构的地址 */
  my.sin_addr.s_addr = inet_addr("10.112.86.5");
  my.sin_port = htons(8080);
  gint ret = bind(rsock, (struct sockaddr *)&my, sizeof(my));
  if(ret < 0){
    g_printerr("bind\n");
    return 1;
  }

  /*开始监听*/
  ret = listen(rsock, 10);
  if(ret < 0){
    g_printerr("listen\n");
    return 1;
  }

  /*通过文件描述字得到GIOChannel*/
  in = g_io_channel_unix_new(rsock);

  /*添加到主循环事件*/
  g_io_add_watch(in,
                 G_IO_IN | G_IO_OUT | G_IO_HUP,
                 (GIOFunc)deal,
                 NULL);
  
  GMainLoop *loop = g_main_loop_new(NULL, FALSE);
  g_main_loop_run(loop);
  
  return 0;
}



2.4 Child Watch

#include<glib.h>

gboolean
source_prepare_cb(GSource *source, gint *timeout){
  g_print("prepare\n");
  *timeout = 1000;
  return FALSE;
}

gboolean
source_check_cb(GSource *source){
  g_print("check\n");
  return TRUE;
}

gboolean
source_dispatch_cb(GSource *source,
                   GSourceFuncs callback,
                   gpointer data){
  g_print("dispatch\n");
  callback(data);
  return TRUE;                  
}

void
source_finalize_cb(GSource *source){
  g_print("finalize\n");
}

void
myidle(gpointer data){

  g_print("myidle\n");
}

int
main(int argc, char *argv[]){
    GMainLoop *loop = g_main_loop_new(NULL, FALSE);
    GMainContext *context = g_main_loop_get_context(loop);

    GSourceFuncs g_source_myidle_funcs = {
        source_prepare_cb,
        source_check_cb,
        source_dispatch_cb,
        source_finalize_cb,
    };

    /* 创建新事件源实例,传入了事件的函数表、事件结构体大小 */
    GSource *source = g_source_new(&g_source_myidle_funcs, sizeof(GSource));
    /* 设置新事件源source的回调函数 */
    g_source_set_callback(source, (GSourceFunc)myidle, "Hello, world!", NULL);
    /* source关联特定的GMainContext对象 */
    g_source_attach(source, context);
    g_source_unref(source);

    g_main_loop_run(loop);

    g_main_context_unref(context);
    g_main_loop_unref(loop);

    return 0;
}

3 代码

3.1 空闲事件源和超时事件源

#include<glib.h>

/**
 * FALSE,该事件源将被删除
 * TRUE,该事件源在没有更高优先级事件时,再次运行
*/
gboolean
count_down(gpointer data){
  static int count = 10;

  if(count < 1){
    g_print("count_down return FALSE\n");
    return FALSE;
  }
  g_print("count_down %4d\n", count--);
  return TRUE;
}

gboolean
cancel_fire(gpointer data){
  GMainLoop *loop = data;
  g_print("cancel_fire() quit \n");
  g_main_loop_quit(loop);

  return FALSE;
}

gboolean
say_idle(gpointer data){
  g_print("say_idle() \n");
  /*该超时事件源只调用一次,因为返回FALSE系统自动删除该事件源*/
  return FALSE;
}

int
main(int argc, char *argv[]){

  /* 每个事件源都会关联一个GMainContext,一个线程只能运行一个GMainContext
   * 但是其他线程中能够对事件源进行添加和删除操作
   */
  GMainContext *context;

  GMainLoop *loop = g_main_loop_new(NULL, FALSE);

  /*添加超时事件源*/
  g_timeout_add(1000, count_down, NULL);
  g_timeout_add(8000, cancel_fire, loop);

  /*添加一个空闲函数*/
  g_idle_add(say_idle, NULL);
  g_main_loop_run(loop);

  g_main_loop_unref(loop);

  return 0;
}

参考1:Glib主事件循环轻度分析与编程应用
参考2:GLib IO Channels Reference Manual

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值