在多线程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本质上是线程安全的吗?如果没有,我需要做什么?
答案 0 :(得分:1)
答案 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;
}
(我没试过这个)
请注意,您需要在所有包装函数中使用相同的互斥锁,因此只有一个线程可以一次进入切片函数。