GLib/GIO 事件源、异步方法与线程的关系

事件源与线程的关系
事件源可以在任意线程中创建并可以 Attach 到任意的线程主上下文(GMainContext)上,Callback 由 Attach 的主线程上下文对应的线程调用。

异步方法与线程的关系
在 GIO 中,一个异步方法通常有两个类型的方法,分别是以 _async 和 _finish 结尾的。_async 方法用于发起一个异步调用,而 _finish 用于取得异步调用的结果。异步方法是使用线程实现的,大概的逻辑是一个线程在 _async 方法中创建新的线程,新线程执行一个事务,完成后通过一定的方法将结果通知给调用线程,调用线程再调用 _finish 方法取得异步方法的结果。

在 GLib 系统中,每个线程都有其主上下文(GMainContext),在一个线程中(主线程或其它线程)调用对象 _async 方法后,会创建一个 GSimpleAsyncResult 类型的对象,该对象用于保存和在调用线程与执行线程间传递数据。接着将方法调用需要的数据(即 _async 方法的参数)“打包“在一个结构体的实例中并设置到 GSimpleAsyncResult 的对象中保存。然后,会调用 g_simple_async_result_run_in_thread 方法并传入一个线程处理函数,该方法通过 GIOSchedulerJob 创建一个新的线程(通过一个专门的线程池),并执行传入的线程处理函数。当线程处理函数执行完成后,会将结果数据保存在 GSimpleAsyncResult 对象中(线程处理函数中实现)。接着会创建一个 GIldeSource 并 Attach 到调用 _async 方法的线程主上下文中,此事件源的 Callback 中会调用 _async 方法中传入的 GAsyncReadyCallback(由调用 _async 方法的线程调用)。最后,应该在 GAsyncReadyCallback 中调用对象的 _finish 方法取得异步调用的结果。

测试程序

/* async.c
 * Heiher <admin@heiher.info>
 * gcc -o async async.c `pkg-config --cflags --libs gio-2.0`
 */
 
#include <gio/gio.h>
 
void enumerate_children_handler (GObject *obj,
			GAsyncResult *res, gpointer user_data)
{
	GFileEnumerator *enumerator = NULL;
 
	enumerator = g_file_enumerate_children_finish (G_FILE (obj),
				res, NULL);
	g_file_enumerator_close_async (enumerator, G_PRIORITY_DEFAULT,
				NULL, NULL, NULL);
	g_object_unref (enumerator);
 
	g_debug ("Handler Thread: %p", g_thread_self ());
}
 
gboolean source_handler (gpointer user_data)
{
	g_debug ("Source Thread: %p", g_thread_self ());
 
	return FALSE;
}
 
gpointer thread_handler (gpointer data)
{
	GMainContext *context = data;
	GSource *source = NULL;
 
	g_debug ("Test Thread: %p", g_thread_self ());
 
	source = g_idle_source_new ();
	g_source_set_priority (source, G_PRIORITY_DEFAULT);
	g_source_set_callback (source, source_handler, NULL, NULL);
	g_source_attach (source, context);
	g_source_unref (source);
 
	return FALSE;
}
 
int main (int argc, char *argv[])
{
	GMainLoop *main_loop = NULL;
 
	g_type_init ();
 
	main_loop = g_main_loop_new (NULL, FALSE);
	if (main_loop) {
		GFile *file = NULL;
 
		file = g_file_new_for_path ("/usr/lib");
 
		g_debug ("Main Thread: %p", g_thread_self ());
 
		g_file_enumerate_children_async (file,
					G_FILE_ATTRIBUTE_STANDARD_NAME,
					G_FILE_QUERY_INFO_NONE,
					G_PRIORITY_DEFAULT,
					NULL,
					enumerate_children_handler,
					NULL);
 
		g_thread_new ("Test", thread_handler,
					g_main_context_default ());
 
		g_main_loop_run (main_loop);
 
		g_object_unref (file);
 
		g_main_loop_unref (main_loop);
	}
 
	return 0;
}

运行结果

** (process:22820): DEBUG: Main Thread: 0x1aa4ca0
** (process:22820): DEBUG: Test Thread: 0x1adac50
** (process:22820): DEBUG: Source Thread: 0x1aa4ca0
** (process:22820): DEBUG: Handler Thread: 0x1aa4ca0

Over!

Leave a Reply

Your email address will not be published. Required fields are marked *