GList(glib-doublely-linked-list)线程是否安全?

时间:2013-05-16 06:05:14

标签: c linux thread-safety glib

在多线程C程序中,我使用了GLib(https://developer.gnome.org/glib/2.35/glib-Doubly-Linked-Lists.html#g-list-append)的GList功能,其中多个线程创建了自己的列表。我观察到不可预测的崩溃,有时一旦应用程序加载。堆栈跟踪显示glist_ *函数中的一些崩溃,如下所示:

(gdb) bt
#0  0x00007fffeb54a964 in g_slice_alloc () from /lib64/libglib-2.0.so.0
#1  0x00007fffeb52aac6 in g_list_append () from /lib64/libglib-2.0.so.0

或者像这样的消息:

  

MEMORY-ERROR:[25628]:GSlice:断言失败:sys_page_size == 0   中止(核心倾销)

     

(进程:15426):GLib-ERROR(递归)**:gmem.c:157:无法分配137438953456>字节   中止...   中止(核心倾销)

我有理由相信GList的引入导致了所有这些崩溃。在单线程程序中,我从未见过这些问题。

GList本质上是线程安全的吗?如果没有,我需要做什么?

4 个答案:

答案 0 :(得分:1)

您使用的是GThread吗?:

  

在调用g_thread_init()之后,GLib完全是线程安全的。

看看this page

答案 1 :(得分:0)

首先,调试符号可能会有所帮助。有关如何获取它们的详细说明,请参阅on gnome live,具体取决于您使用的是哪种发行版。

如前所述,g_slice API是线程安全的。 IIRC它被设计成无锁或非常接近。如前所述,GLib数据结构通常可以安全地在多线程环境中使用(只要不同时从多个线程访问实例) - 如果不是,那么它就是一个应该报告的错误upstream(但是由于GLib被广泛使用,包括多线程环境,它不太可能有一些明显的错误。)

鉴于堆栈跟踪,它看起来像内存损坏。我猜你在某处有一个缓冲区溢出/下溢并写入g_slice内部存储器或你使用未初始化的GList指针具有类似访问'随机存储器'的效果或者你试图传递一个负值来分配由于某些原因导致溢出整数。我建议在Valgrind下使用G_SLICE=always-malloc。如果它太慢了,还有其他方法,比如gcc 4.8+和clang中的AddressSanitizer(我不记得哪个版本)。请注意,此类错误可能不在与GList相关的代码中,而是由微妙的交互(不同的地址布局等)引起。

一旦你完成它(使用调试符号并使用valgrind运行),你应该更好地了解发生了什么以及bug可能在哪里。请注意,它不会得到所有错误,但它会对大多数常见情况有所帮助。

答案 2 :(得分:0)

引用this page中的文档:

GLib的一般策略是,除了数据结构操作函数外,所有函数都是隐式线程安全的,其中,如果有两个线程在操纵同一数据结构,则它们必须使用锁来同步其操作。 GLib出于自己的目的创建了一个工作线程,因此GLib应用程序将始终至少有2个线程。

答案 3 :(得分:-1)

GList,与大多数其他简单的GLib数据结构一样,不是线程安全的。但是,由于您没有同时从多个线程修改相同的列表,因此应该没有问题。我认为实际导致你的段错误的是多次并发调用g_slice_alloc(由g_list_append / prepend调用产生)。其他人似乎之前碰到了same problem

我解决这个问题的方法是为每个g_list创建一个互斥保护包装函数,分配你正在使用的内存(让我们以g_list_append为例),然后写:

/* Init this before starting your threads with g_mutex_new() */
static GMutex *g_list_mutex;

GList *safe_list_append(GList *list, gpointer data)
{
    GList *ret;

    g_mutex_lock(g_list_mutex);
    ret = g_list_append(list, data);
    g_mutex_unlock(g_list_mutex);

    return ret;
}

(我没试过这个)

请注意,您需要在所有包装函数中使用相同的互斥锁,因此只有一个线程可以一次进入切片函数。