- 論壇徽章:
- 0
|
我遇到的這個問題其實應該不算個問題,但在我剛開始學多線程編程的時候卻困擾了我很久,在網上也沒找到答案,或者說沒有直接的答案,后來是自己去讀了部分nptl源代碼后才明白原因,雖然費時很多,但也收獲良多。
問題很簡單,就是關于線程取消的操作,在某種情況下,線程不管怎樣都不能退出,問題肯定是出在鎖上,但始終找不到死鎖的地方,先帖段代碼:- #include <stdio.h>
- #include <unistd.h>
- #include <pthread.h>
- pthread_mutex_t lock;
- pthread_cond_t cond;
- void thrd_cleanup()
- {
- printf("thread canceled\n");
- pthread_mutex_unlock(&lock);
- }
- void thrd_func()
- {
- pthread_cleanup_push(thrd_cleanup, NULL);
- printf("thread: enter\n");
- pthread_mutex_lock(&lock);
- pthread_cond_wait(&cond, &lock);
- pthread_mutex_unlock(&lock);
- pthread_cleanup_pop(0);
- printf("thread: exit\n");
- }
- int main(int argc, char *argv[])
- {
- pthread_t tid[4];
- pthread_mutex_init(&lock, NULL);
- pthread_cond_init(&cond, NULL);
- for (int i = 0; i < 4; ++i) {
- int ret = pthread_create(&tid[i], NULL, thrd_func, NULL);
- if (ret) {
- perror("pthread_create");
- }
- }
- sleep(3);
- pthread_mutex_lock(&lock);
- for (int i = 0; i < 4; ++i) {
- printf("before cancel\n");
- pthread_cancel(tid[i]);
- pthread_join(tid[i], NULL);
- printf("after cancel\n");
- }
- pthread_mutex_unlock(&lock);
- sleep(6);
- return 0;
- }
復制代碼 上面代碼得到的輸出為- thread: enter
- thread: enter
- thread: enter
- thread: enter
- before cancel
復制代碼 可見在第一個線程退出的時候出了問題,一開始就幾乎可以肯定是鎖這出了問題,可一直找不到原因,attach該進程,得到的結果為- (gdb) bt
- #0 0xb772f424 in __kernel_vsyscall ()
- #1 0xb7707e1c in pthread_join () from /lib/i386-linux-gnu/libpthread.so.0
- #2 0x080489f4 in main (argc=1, argv=0xbfe68a24) at main.c:67
復制代碼 可見,程序在調用pthread_join后阻塞了,再查看當前進程中的線程:- (gdb) info threads
- Id Target Id Frame
- 4 Thread 0xb6d52b40 (LWP 5658) "foo" 0xb772f424 in __kernel_vsyscall ()
- 3 Thread 0xb6551b40 (LWP 5659) "foo" 0xb772f424 in __kernel_vsyscall ()
- 2 Thread 0xb5d50b40 (LWP 5660) "foo" 0xb772f424 in __kernel_vsyscall ()
- * 1 Thread 0xb75546c0 (LWP 5656) "foo" 0xb772f424 in __kernel_vsyscall ()
復制代碼 顯示已經有一個線程成功退出,而查看進程的/proc文件系統(tǒng)信息得到的卻是:- $ls /proc/5656/task
- 5656 5657 5658 5659 5660
復制代碼 這卻說明剛才的那個線程沒有完全退出,當時我覺得這是相當詭異的,所謂百思不得其解啊,最后經過分析nptl關于pthread_cancel操作的源碼后,終于找到了原因:
我們在進入線程的時候注冊了線程清理理函數(shù),隨后進入等待狀態(tài),注意這時候主線程已經持有鎖了,并調用pthread_cancel和pthread_join,這個時候nptl的實現(xiàn)中需要再次持有該鎖,這時候問題就出現(xiàn)了,主線程持有鎖并等待pthread_join的結束,而nptl庫需要先拿到鎖才能返回,這就永遠不會結束,導致上面的問題。
經過這個問題之后,覺得多線程編程真是個頭疼的問題,稍有不甚就會出現(xiàn)各種詭異的問題,尤其對于初學者,經驗不足,解決起這些問題是費時費力又不一定有效果。
|
評分
-
查看全部評分
|