glib源码下载:http://ftp.gnome.org/pub/gnome/sources/glib/
glib帮助文档:https://developer.gnome.org/glib/
主事件循环 - 管理所有可用的事件源
Includes
#include <glib.h>
描述
主事件循环管理GLib和GTK +应用程序的所有可用事件源。 这些事件可以来自任意数量的不同类型的源,例如文件描述符(普通文件,管道或套接字)和超时。 还可以使用g_source_attach()添加新类型的事件源。
为了允许在不同的线程中处理多个独立的源集,每个源与GMainContext相关联。 一个GMainContext只能在单个线程中运行,但可以将源添加到其中并在其他线程中删除。
为每个事件源分配优先级。 默认优先级G_PRIORITY_DEFAULT为0.小于0的值表示更高的优先级。 大于0的值表示较低的优先级。 来自高优先级源的事件始终在来自较低优先级源的事件之前处理。
还可以添加空闲函数,并为其分配优先级。 只要没有准备好处理具有更高优先级的事件,就会运行这些操作。
GMainLoop数据类型表示主事件循环。 使用g_main_loop_new()创建GMainLoop。 添加初始事件源后,将调用g_main_loop_run()。 这会不断检查每个事件源的新事件并发送它们。 最后,处理来自其中一个源的事件会导致调用g_main_loop_quit()以退出主循环,并返回g_main_loop_run()。
可以递归地创建GMainLoop的新实例。 在显示模态对话框时,这通常用于GTK +应用程序。 请注意,事件源与特定的GMainContext相关联,并将针对与该GMainContext关联的所有主循环进行检查和分派。
GTK +包含一些这些函数的包装器,例如 gtk_main(),gtk_main_quit()和gtk_events_pending()。
创建新的源类型
GMainLoop功能的一个不同寻常的功能是除了内置类型的事件源之外,还可以创建和使用新类型的事件源。 新事件源类型用于处理GDK事件。 通过从GSource结构“派生”创建新的源类型。 派生类型的源由一个结构表示,该结构具有GSource结构作为第一个元素,以及特定于新源类型的其他元素。 要创建新源类型的实例,请调用g_source_new()传递派生结构的大小和函数表。 这些GSourceFuncs确定新源类型的行为。
新的源类型基本上以两种方式与主上下文交互。 它们在GSourceFuncs中的准备函数可以设置超时,以确定主循环在再次检查源之前将休眠的最长时间。 此外,或者,源也可以将文件描述符添加到主上下文使用g_source_add_poll()检查的集合中。
自定义主循环迭代
可以使用g_main_context_iteration()运行GMainContext的单次迭代。 在某些情况下,需要更详细地控制主循环的细节如何工作,例如,将GMainLoop与外部主循环集成时。 在这种情况下,您可以直接调用g_main_context_iteration()的组件函数。 这些函数是g_main_context_prepare(),g_main_context_query(),g_main_context_check()和g_main_context_dispatch()。
Main Context的状态
这些功能的操作最好从状态图中看出,如该图所示。
在UNIX上,GLib主循环与fork()不兼容。 任何使用mainloop的程序都必须从子进程执行exec()或exit()而不返回mainloop。
内存管理源
传递给GSource的用户数据的内存管理有两种选择,可以在调用时传递给它的回调。 在调用g_timeout_add(),g_timeout_add_full(),g_idle_add()等时提供此数据,更一般地,使用g_source_set_callback()。 该数据通常是“拥有”超时或空闲回调的对象,例如小部件或网络协议实现。 在许多情况下,在此拥有对象被销毁之后调用回调是错误的,因为这会导致使用释放的内存。
第一个也是首选的选项是存储由g_timeout_add()或g_source_attach()等函数返回的源ID,并在最终确定拥有对象时使用g_source_remove()从主上下文中显式删除该源。 这确保了只有在对象仍然活动时才能调用回调。
第二个选项是在回调中保持对对象的强引用,并在回调的GDestroyNotify中释放它。 这可确保对象保持活动状态,直到源完成后,保证在最后一次调用之后。 GDestroyNotify是传递给GSource函数的“完整”变体的另一个回调(例如,g_timeout_add_full())。 它在源完成时调用,用于释放这样的引用。
第二种方法的一个重要警告是,如果在调用GSource之前停止主循环,它将使对象无限期地保持活动,这可能是不合需要的。
该部分有很多函数,但是能力有限只能使用皮毛,下面就简单的使用一下定时器的功能。其实,真正最有用的应该是如何自己去创建source类型,和自己去控制事件调度。
函数也就条最简单的一些进行翻译和举例。
Functions
g_main_loop_new ()
创建一个新的GMainLoop结构。
参数
GMainContext(如果为NULL,将使用默认上下文)。
is_running
设置为TRUE表示循环正在运行。 这不是很重要,因为无论如何调用g_main_loop_run()都会将其设置为TRUE。
g_main_loop_run ()
运行主循环,直到在循环上调用g_main_loop_quit()。 如果为循环的GMainContext的线程调用它,它将处理来自循环的事件,否则它将只是等待。
g_main_loop_quit ()
停止运行GMainLoop。 对循环的任何g_main_loop_run()调用都将返回。
请注意,g_main_loop_quit()被调用时,已调度的源将仍然执行。
g_main_loop_is_running ()
检查主循环当前是否通过g_main_loop_run()运行。
返回值
如果当前正在运行主循环,则为TRUE。
g_main_loop_get_context ()
返回循GMainContext的loop。
g_main_context_new ()
创建一个新的GMainContext结构。
g_main_context_default ()
返回全局默认主上下文。 当未明确指定主循环时,这是用于主循环函数的主要上下文,并且对应于“main”主循环。 另请参见g_main_context_get_thread_default()。
返回值
全局默认main context。
g_main_context_acquire ()
试图成为指定上下文的所有者。 如果某个其他线程是上下文的所有者,则立即返回FALSE。 所有权是正确的递归:所有者可以再次要求所有权,并且在g_main_context_release()被调用多次g_main_context_acquire()时将释放所有权。
Y您必须是上下文的所有者才能调用g_main_context_prepare(),g_main_context_query(),g_main_context_check(),g_main_context_dispatch()。
返回值
如果操作成功,则为TRUE,此线程现在是上下文的所有者。
g_main_context_release ()
使用g_main_context_acquire()释放此线程先前获取的上下文的所有权。 如果多次获取上下文,则只有在调用g_main_context_release()多次时才会释放所有权。
g_main_context_is_owner ()
确定此线程是否包含此GMainContext的(递归)所有权。 在等待可能阻止获取上下文所有权的另一个线程之前,这很有用。
返回值
如果当前线程是上下文的所有者,则为TRUE。
g_main_current_source ()
返回此线程的当前触发源。
返回值
当前触发源或NULL。
g_timeout_source_new ()
创建新的超时源。
源最初不会与任何GMainContext相关联,必须在执行之前将其添加到g_source_attach()中。
给定的间隔是monotonic时间,而不是clock时间。 请参阅g_get_monotonic_time()。
参数
interval
超时间隔(以毫秒为单位)。
返回值
新创建的超时源
g_timeout_source_new_seconds ()
创建新的超时源。
源最初不会与任何GMainContext相关联,必须在执行之前将其添加到g_source_attach()中。
此超时源的调度粒度/准确度将以秒为单位。
以单调时间给出的间隔,而不是挂钟时间。 请参阅g_get_monotonic_time()。
参数
interval
超时间隔(秒)
返回值
新创建的超时源
g_timeout_add ()
设置要定期调用的函数,默认优先级为G_PRIORITY_DEFAULT。 重复调用该函数,直到它返回FALSE,此时超时自动销毁,并且不会再次调用该函数。 对函数的第一次调用将在第一个间隔结束时进行。
请注意,由于处理其他事件源,超时功能可能会延迟。 因此,不应依赖它们来进行精确计时。在每次调用超时函数之后,将根据当前时间和给定间隔重新计算下一次超时的时间(它不会尝试“赶上”延迟时间丢失)。
有关如何处理数据的返回值和内存管理的详细信息,请参阅源的内存管理。
如果你想在“秒”范围内有一个计时器而不关心第一次调用计时器的确切时间,请使用g_timeout_add_seconds()函数; 此功能允许更多优化和更高效的系统功耗。
这在内部使用g_timeout_source_new()创建一个主循环源,并使用g_source_attach()将其附加到全局GMainContext,因此将在运行该主上下文的任何线程中调用回调。 如果需要更好的控制或使用自定义主上下文,则可以手动执行这些步骤。
给定的间隔是monotonic时间,而不是clock时间。 请参阅g_get_monotonic_time()。
参数
调用函数之间的时间,以毫秒为单位(1/1000秒)
返回值
事件源的ID(大于0)。
g_timeout_add_full ()
设置要以给定优先级定期调用的函数。 重复调用该函数,直到它返回FALSE,此时超时自动销毁,并且不会再次调用该函数。 超时被销毁时调用notify函数。 对函数的第一次调用将在第一个间隔结束时进行。
请注意,由于处理其他事件源,超时功能可能会延迟。 因此,不应依赖它们来进行精确计时。 在每次调用超时函数之后,将根据当前时间和给定间隔重新计算下一次超时的时间(它不会尝试“赶上”延迟时间丢失)。
有关如何处理数据的返回值和内存管理的详细信息,请参阅源的内存管理。
这在内部使用g_timeout_source_new()创建一个主循环源,并使用g_source_attach()将其附加到全局GMainContext,因此将在运行该主上下文的任何线程中调用回调。 如果需要更好的控制或使用自定义主上下文,则可以手动执行这些步骤。
以monotonic时间给出的间隔,而不是clock时间。 请参阅g_get_monotonic_time()。
参数
priority
超时源的优先级。 通常,这将在G_PRIORITY_DEFAULT和G_PRIORITY_HIGH之间的范围内。
interval
调用函数之间的时间,以毫秒为单位(1/1000秒)
notify
删除超时时调用的函数,或NULL。
返回值
事件源的ID(大于0)。
g_timeout_add_seconds ()
设置要定期调用的函数,使用默认优先级G_PRIORITY_DEFAULT。 重复调用该函数,直到它返回FALSE,此时超时自动销毁,并且不会再次调用该函数。
这在内部使用g_timeout_source_new_seconds()创建一个主循环源,并使用g_source_attach()将其附加到主循环上下文。 如果需要更好的控制,可以手动执行这些步骤。 另请参阅g_timeout_add_seconds_full()。
请注意,定时器的第一次调用对于一秒钟的超时可能不准确。 如果您需要更精确的并且具有这样的超时,则可能需要使用g_timeout_add()。
有关如何处理数据的返回值和内存管理的详细信息,请参阅源的内存管理。
The interval given is in terms of monotonic time, not wall clock time. See g_get_monotonic_time().
给定的间隔是monotonic时间,而不是clock时间。 请参阅g_get_monotonic_time()。
参数
interval
调用函数之间的时间,以秒为单位
返回值
事件源的ID(大于0)。
g_idle_source_new ()
创建一个新的空闲源。
源最初不会与任何GMainContext相关联,必须在执行之前将其添加到g_source_attach()中。 请注意,与其他具有默认优先级G_PRIORITY_DEFAULT的源相比,空闲源的默认优先级为G_PRIORITY_DEFAULT_IDLE。
返回值
新创建的空闲源
g_idle_add ()
每当没有更高优先级事件挂起到默认主循环时,添加要调用的函数。 该函数具有默认的空闲优先级G_PRIORITY_DEFAULT_IDLE。 如果函数返回FALSE,它将自动从事件源列表中删除,不会再次调用。
有关如何处理数据的返回值和内存管理的详细信息,请参阅源的内存管理。
这在内部使用g_idle_source_new()创建一个主循环源,并使用g_source_attach()将其附加到全局GMainContext,因此将在运行该主上下文的任何线程中调用回调。 如果需要更好的控制或使用自定义主上下文,则可以手动执行这些步骤。
返回值
事件源的ID(大于0)。
g_source_attach ()
将GSource添加到上下文,以便在该上下文中执行。 通过调用g_source_destroy()删除它。
参数
context
GMainContext(如果为NULL,将使用默认上下文)。
返回值
GMainContext中源的ID(大于0)。
g_source_destroy ()
从其GMainContext中删除源(如果有),并将其标记为已销毁。 源不能随后添加到另一个上下文。 在已经从其上下文中删除的源上调用它是安全的。
g_source_is_destroyed ()
返回源是否已被销毁。
当您从空闲处理程序中操作对象时,这很重要,但可能在分配空闲处理程序之前释放了对象。
static gboolean
idle_callback (gpointer data)
{
SomeWidget *self = data;
GDK_THREADS_ENTER ();
// do stuff with self
self->idle_id = 0;
GDK_THREADS_LEAVE ();
return G_SOURCE_REMOVE;
}
static void
some_widget_do_stuff_later (SomeWidget *self)
{
self->idle_id = g_idle_add (idle_callback, self);
}
static void
some_widget_finalize (GObject *object)
{
SomeWidget *self = SOME_WIDGET (object);
if (self->idle_id)
g_source_remove (self->idle_id);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
如果在空闲处理程序因回调中空闲后使用而触发之前销毁小部件,则在多线程应用程序中将失败。 对于这个特定问题,解决方案是检查源是否已在回调中销毁。
static gboolean
idle_callback (gpointer data)
{
SomeWidget *self = data;
GDK_THREADS_ENTER ();
if (!g_source_is_destroyed (g_main_current_source ()))
{
// do stuff with self
}
GDK_THREADS_LEAVE ();
return FALSE;
}
从GSource附加的GMainContext获取的线程以外的线程调用此函数通常是多余的,因为在此函数返回后可能会立即销毁源。 但是,一旦源被销毁,它就不能被销毁,所以这个函数可以用于任何线程的机会性检查。
返回值
如果源已被破坏,则为TRUE
g_source_set_priority ()
设置源的优先级。 在运行主循环时,如果准备好调度源,则将调度源,并且不准备分派更高(数值更小)优先级的源。
子源始终与其父源具有相同的优先级。 一旦将源添加为另一个源的子级,就不允许更改源的优先级。
g_source_get_priority ()
获取源的优先级。
返回值
来源的优先权
g_source_get_id ()
返回特定源的数字ID。 源的ID是正整数,其在特定主循环上下文中是唯一的。 从ID到源的反向映射由g_main_context_find_source_by_id()完成。
返回值
源的ID(大于0)
g_source_get_name ()
获取源的名称,用于调试和分析。 如果从未使用g_source_set_name()设置该名称,则该名称可能为NULL。
返回值
来源的名称
g_source_set_name ()
设置源的名称,用于调试和分析。 名称默认为NULL。
源名称应以人类可读的方式描述源的作用。 例如,“X11事件队列”或“GTK +重新绘制空闲处理程序”或其他任何内容。
允许多次调用此函数,但由于潜在的性能影响,不建议使用此函数。 例如,可以更改GSourceFuncs的“check”函数中的名称,以包含源名称中的事件类型等详细信息。
如果更改名称时要小心,而另一个线程可能正在使用g_source_get_name()访问它; 该函数不复制该值,并且更改该值将释放它,而另一个线程可能正在尝试使用它。
g_source_set_name_by_id ()
使用其ID设置源的名称。
这是一个便利实用程序,用于从g_idle_add(),g_timeout_add()等的返回值设置源名称。
尝试设置不存在的源的名称是程序员错误。
更具体地说:在源被销毁之后可以重新发布源ID,因此使用具有可能已被移除的源ID的此函数永远无效。 一个例子是当使用g_idle_add()调度空闲以在另一个线程中运行时:空闲可能已经运行并且在其函数(现在无效的)源ID上被调用时被删除。 此源ID可能已重新发布,导致针对错误的源执行操作。
g_source_get_context ()
获取与源关联的GMainContext。
你可以在已被销毁的源上调用它,只要它附加的GMainContext仍然存在(在这种情况下它将返回该GMainContext)。 特别是,您始终可以在从g_main_current_source()返回的源上调用此函数。 但是在GMainContext被销毁的源上调用此函数是一个错误。
返回值
与源关联的GMainContext,如果尚未将上下文添加到源,则为NULL。
g_source_set_callback ()
设置源的回调函数。 源的回调函数从源的调度函数调用。
func的确切类型取决于源的类型;即。 你不应指望用数据作为第一个参数调用func。
有关如何处理数据内存管理的详细信息,请参阅源的内存管理。
通常,您不会使用此功能。 而是使用特定于您正在使用的源类型的函数。
GSourceFunc ()
指定传递给g_timeout_add(),g_timeout_add_full(),g_idle_add()和g_idle_add_full()的函数类型。
参数
user_data
传递给函数的数据,在使用上述函数之一创建源时设置
返回值
如果应删除源,则为FALSE。 G_SOURCE_CONTINUE和G_SOURCE_REMOVE是返回值的更令人难忘的名称。
g_source_get_time ()
获取检查此源时使用的时间。 调用此函数直接调用g_get_monotonic_time()的优点是,在检查多个源时,GLib可以缓存单个值,而不必重复获取系统monotonic时间。
这里的时间是系统monotonic时间(如果可用),或者其他一些合理的替代方案。 请参阅g_get_monotonic_time()。
g_source_remove ()
从默认主上下文中删除具有给定ID的源。 必须将g_source_destroy()用于添加到非默认主上下文的源。
GSource的ID由g_source_get_id()给出,或者由函数g_source_attach(),g_idle_add(),g_idle_add_full(),g_timeout_add(),g_timeout_add_full(),g_child_watch_add(),g_child_watch_add_full(),g_io_add_watch()返回。 和g_io_add_watch_full()。
尝试删除不存在的源是程序员错误。
更具体地说:在源被销毁之后可以重新发布源ID,因此使用具有可能已被移除的源ID的此函数永远无效。 一个例子是当使用g_idle_add()调度空闲以在另一个线程中运行时:空闲可能已经运行并且在其函数(现在无效的)源ID上被调用时被删除。 此源ID可能已重新发布,导致针对错误的源执行操作。
返回值
由于历史原因,此函数始终返回TRUE
Types and Values
GMainLoop
GMainLoop结构是一种不透明的数据类型,表示GLib或GTK +应用程序的主事件循环。
G_PRIORITY_HIGH
#define G_PRIORITY_HIGH -100
将其用于高优先级事件源。
它不在GLib或GTK +中使用。
G_PRIORITY_DEFAULT
#define G_PRIORITY_DEFAULT 0
将其用于默认优先级事件源。
在GLib中,当使用g_timeout_add()添加超时函数时,将使用此优先级。 在GDK中,此优先级用于来自X服务器的事件。
G_PRIORITY_HIGH_IDLE
#define G_PRIORITY_HIGH_IDLE 100
用于高优先级空闲功能。
GTK +使用G_PRIORITY_HIGH_IDLE + 10来调整操作,使用G_PRIORITY_HIGH_IDLE + 20来重绘操作。 (这样做是为了确保在任何挂起的重绘之前处理任何挂起的调整大小,这样小部件就不会被不必要地重绘两次。)
G_PRIORITY_DEFAULT_IDLE
#define G_PRIORITY_DEFAULT_IDLE 200
用于默认优先级空闲功能。
在GLib中,在使用g_idle_add()添加空闲函数时使用此优先级。
G_PRIORITY_LOW
#define G_PRIORITY_LOW 300
将此用于非常低优先级的后台任务。
它不在GLib或GTK +中使用。
G_SOURCE_CONTINUE
#define G_SOURCE_CONTINUE TRUE
使用此宏作为GSourceFunc的返回值,以将GSource保留在主循环中。
G_SOURCE_REMOVE
#define G_SOURCE_REMOVE FALSE
使用此宏作为GSourceFunc的返回值,以从主循环中删除GSource。
示例代码:
#include <glib.h>
#include <glib/gprintf.h>
gboolean gsource_func (gpointer user_data)
{
g_printf ("%s() in\n", __func__);
GTimeVal result;
g_get_current_time (&result);
g_printf ("gsource_func %ld.%lds\n", result.tv_sec, result.tv_usec);
int *data = (int *)user_data;
(*data)++;
g_printf ("user_data = %d\n", (*data));
g_printf ("%s() out\n", __func__);
if ((*data) == 10)
return FALSE;
return TRUE;
}
gboolean gsource_func1 (gpointer user_data)
{
g_printf ("%s() in\n", __func__);
GTimeVal result;
g_get_current_time (&result);
g_printf ("gsource_func1 %ld.%lds\n", result.tv_sec, result.tv_usec);
g_printf ("%s() out\n", __func__);
return TRUE;
}
gboolean gsource_idle (gpointer user_data)
{
g_printf ("%s() in\n", __func__);
g_printf ("%s() out\n", __func__);
return FALSE;
}
void gdestroy_notify (gpointer data)
{
g_printf ("%s() in\n", __func__);
g_free (data);
g_printf ("%s() out\n", __func__);
}
gpointer thread_func (gpointer data)
{
g_printf ("%s() in\n", __func__);
GMainContext *context = g_main_context_new ();
GMainLoop *loop = g_main_loop_new (context, FALSE);
g_printf ("context is %sowner\n", g_main_context_is_owner (context)?"":"not ");
g_main_context_acquire (context);
g_printf ("context is %sowner\n", g_main_context_is_owner (context)?"":"not ");
GSource *source = g_timeout_source_new_seconds (2);
guint sourceID = g_source_attach (source, context);
g_source_set_name (source, "thread_timeout");
g_printf ("source_get_name:%s ID:%d\n", g_source_get_name (source), sourceID);
int *data1 = g_new0(int, 1);
g_source_set_callback (source, gsource_func, data1, gdestroy_notify);
g_main_loop_run (loop);
g_printf ("%s() out\n", __func__);
}
gpointer thread_func1 (gpointer data)
{
g_printf ("%s() in\n", __func__);
GMainContext *context = g_main_context_new ();
GMainLoop *loop = g_main_loop_new (context, FALSE);
g_printf ("context is %sowner\n", g_main_context_is_owner (context)?"":"not ");
g_main_context_acquire (context);
g_printf ("context is %sowner\n", g_main_context_is_owner (context)?"":"not ");
GSource *source = g_timeout_source_new_seconds (2);
guint sourceID = g_timeout_add (2500, gsource_func1, NULL);
g_source_set_name (source, "thread_timeout1");
g_printf ("source_get_name:%s ID:%d\n", g_source_get_name (source), sourceID);
g_main_loop_run (loop);
g_printf ("%s() out\n", __func__);
}
int main(int argc, char **argv)
{
g_printf ("%s() in\n", __func__);
GThread *gthread = g_thread_new ("func", thread_func, NULL);
GThread *gthread1 = g_thread_new ("func1", thread_func1, NULL);
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
GSource *source = g_timeout_source_new (1500);//1.5s
guint sourceID = g_source_attach (source, NULL);
g_printf ("source_get_ID:%d\n", sourceID);
g_source_set_callback (source, gsource_func1, NULL, NULL);
g_idle_add (gsource_idle, NULL);
g_main_loop_run (loop);
g_printf ("%s() out\n", __func__);
}
执行输出:
renz@media-Lenovo-Product:~/glibtest$ ./loop
main() in
source_get_ID:1
gsource_idle() in
thread_func() in
gsource_idle() out
thread_func1() in
context is not owner
context is owner
context is not owner
context is owner
source_get_name:thread_timeout1 ID:3
source_get_name:thread_timeout ID:1
gsource_func1() in
gsource_func1 1559725211.221778s
gsource_func1() out
gsource_func() in
gsource_func 1559725211.816416s
user_data = 1
gsource_func() out
gsource_func1() in
gsource_func1 1559725212.222865s
gsource_func1() out
gsource_func1() in
gsource_func1 1559725212.722452s
gsource_func1() out
gsource_func() in
gsource_func 1559725213.816440s
user_data = 2
gsource_func() out
gsource_func1() in
gsource_func1 1559725214.224010s
gsource_func1() out
gsource_func1() in
gsource_func1 1559725214.723595s
gsource_func1() out
gsource_func1() in
gsource_func1 1559725215.725656s
gsource_func1() out
gsource_func() in
gsource_func 1559725215.816543s
user_data = 3
gsource_func() out
gsource_func1() in
gsource_func1 1559725217.225192s
gsource_func1() out
gsource_func1() in
gsource_func1 1559725217.226268s
gsource_func1() out
…………………………