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