2013-05-16 93 views
2

在一個多線程C程序中,我使用了GLib的GList功能(https://developer.gnome.org/glib/2.35/glib-Doubly-Linked-Lists.html#g-list-append),其中多個線程創建了自己的列表。我觀察到不可預知的崩潰,有時一旦加載應用程序。堆棧跟蹤顯示glist_ *函數的一些崩潰的一些消息是這樣的:GList(glib-doubly-linked-list)線程安全嗎?

(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: assertion failed: sys_page_size == 0 Aborted (core dumped)

(process:15426): GLib-ERROR (recursed) **: gmem.c:157: failed to allocate 137438953456 >bytes aborting... Aborted (core dumped)

我有理由相信,引進爲Glist使這一切的崩潰。在單線程程序中,我從來沒有見過這些問題。

GList本質上是線程安全的嗎?如果不是,我需要做什麼?

回答

2

您使用的g線程?:

After calling g_thread_init(), GLib is completely thread safe.

看看到this page

+0

我不這樣做,我用POSIX線程(pthread_create等)。在產生子線程之前,我應該使用g_thread_init(),以NULL作爲參數,來自父線程嗎? –

+1

不,您必須使用g_thread系列函數,另一方面如果使用POSIX樣式線程,則定義'G_THREADS_IMPL_POSIX' –

+1

請繼續閱讀:「GLib完全是線程安全的(所有全局數據都會自動鎖定),但個別數據結構例如,你必須協調從多個線程訪問同一個GHashTable,這個規則的兩個值得注意的例外是GMainLoop和GAsyncQueue,它們是線程安全的,不需要進一步的應用程序級鎖定從多個線程訪問「。你說線程*創建了自己的列表,但它們是從多個線程併發訪問的嗎? – nemequ

-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; 
} 

(我沒有測試過)

請注意,您需要在所有包裝函數中使用相同的互斥鎖,因此一次只能有一個線程進入切片函數。

+0

g_slice api應該是線程安全的(參見nemequ評論,IIRC它在內部是無鎖的) - 如果它崩潰,那麼a)glib中存在應該向上游報告的錯誤在https://bugzilla.gnome.org/中使用的泛濫程度)或者b)程序intself中存在缺陷 - 因爲'G_SLICE = always-malloc'也出現了喜歡的問題,所以它更加可能,否則它將意味着,但在系統malloc和切片。至於你的解決方案 - 你只保護單一的呼叫,這樣釋放內存和追加將同時完成。 –

+0

所以,即使在切片API和malloc中存在問題,它也不會保護它。 –

+0

>「你只保護單個電話,這樣釋放內存和追加將同時完成」 - 我不明白這一點,你能澄清嗎? – Ancurio

0

首先,調試符號可能會有所幫助。有關如何獲取它們的詳細說明,請參閱on gnome live,並取決於您使用的是哪種發行版。

如前所述,g_slice API是線程安全的。 IIRC它被設計成無鎖或非常接近於此。 GLib數據結構通常在多線程環境中使用是安全的(如同時注意到的,只要實例不是從多個線程同時訪問) - 如果它們不是,那麼它應該被報告爲upstream(但是由於GLib廣泛使用包括多線程環境,它是不太可能的,它有一些明顯的錯誤)。

鑑於堆棧跟蹤,它看起來像內存損壞。我的猜測是,你有一個緩衝區溢出/下溢的地方,並寫在g_slice內部內存或您使用未初始化的GList指針具有類似的訪問'隨機存儲'的效果,或者你嘗試傳遞一個負值分配由於某些原因導致溢出的整數。我建議在Valgrind下運行G_SLICE=always-malloc。如果速度太慢,還有其他方法,例如gcc 4.8+和clang中的AddressSanitizer(我不記得哪個版本)。請注意,此類錯誤可能與代碼不相關,但由微妙的交互(不同的地址佈局等)引起。

一旦你完成了它(使用調試符號並使用valgrind運行),你應該對發生了什麼以及錯誤的可能性有更多的瞭解。請注意,它不會得到所有的錯誤,但它會幫助最常見的情況。

+0

簡單的數據結構(如GList)默認情況下不是**線程安全的。如果你看看源代碼,你會發現沒有併發保護。 – Ancurio

+0

@Ancurio:對不起。澄清 - 我的意思是API不是實例(即可安全地在多線程環境中使用,而不是線程安全的)。 –