- 论坛徽章:
- 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文件系统信息得到的却是:- $ls /proc/5656/task
- 5656 5657 5658 5659 5660
复制代码 这却说明刚才的那个线程没有完全退出,当时我觉得这是相当诡异的,所谓百思不得其解啊,最后经过分析nptl关于pthread_cancel操作的源码后,终于找到了原因:
我们在进入线程的时候注册了线程清理理函数,随后进入等待状态,注意这时候主线程已经持有锁了,并调用pthread_cancel和pthread_join,这个时候nptl的实现中需要再次持有该锁,这时候问题就出现了,主线程持有锁并等待pthread_join的结束,而nptl库需要先拿到锁才能返回,这就永远不会结束,导致上面的问题。
经过这个问题之后,觉得多线程编程真是个头疼的问题,稍有不甚就会出现各种诡异的问题,尤其对于初学者,经验不足,解决起这些问题是费时费力又不一定有效果。
|
评分
-
查看全部评分
|