C pthread_join()挂起在压力测试中
我正在运行一个phread测试程序,直到它失败。以下是代码的主要框架:C pthread_join()挂起在压力测试中,c,pthreads,C,Pthreads,我正在运行一个phread测试程序,直到它失败。以下是代码的主要框架: int authSessionListMutexUnlock() { int rc = 0; int rc2 = 0; rc2 = pthread_mutex_trylock(&mutex); ERR_IF( rc2 != EBUSY && rc2 != 0 ); rc2 = pthread_mutex_unlock(&mutex); ERR
int authSessionListMutexUnlock()
{
int rc = 0;
int rc2 = 0;
rc2 = pthread_mutex_trylock(&mutex);
ERR_IF( rc2 != EBUSY && rc2 != 0 );
rc2 = pthread_mutex_unlock(&mutex);
ERR_IF( rc2 != 0 );
cleanup:
return rc;
}
static void cleanup_handler(void *arg)
{
int rc = 0;
(void)arg;
rc = authSessionListMutexUnlock();
if (rc != 0)
AUTH_DEBUG5("authSessionListMutexUnlock() failed\n");
}
static void *destroy_expired_sessions(void *t)
{
int rc2 = 0;
(void)t;
pthread_cleanup_push(cleanup_handler, NULL);
rc2 = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (rc2 != 0)
AUTH_DEBUG5("pthread_setcancelstate(): rc2 == %d\n", rc2);
rc2 = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
if (rc2 != 0)
AUTH_DEBUG5("pthread_setcanceltype(): rc2 == %d\n", rc2);
while (1)
{
... // destroy expired session
sleep(min_timeout);
}
pthread_cleanup_pop(0);
}
int authDeinit( char *path )
{
...
rc2 = authSessionListDeInit();
ERR_IF( rc2 != 0 );
rc2 = pthread_cancel(destroy_thread);
ERR_IF( rc2 != 0 );
rc2 = pthread_join(destroy_thread, &status);
ERR_IF( rc2 != 0 || (int *)status != PTHREAD_CANCELED );
...
return 0
}
它在测试程序中运行良好,但在pthread_join()中测试程序在第#53743轮挂起:
看起来pthread_join()导致了死锁。但是看看代码,我觉得没有理由认为pthread_join()会导致死锁。当pthread_join()有机会运行时,唯一的互斥操作就是线程本身。应该没有冲突吧?这里真的很困惑…在代码中至少显示了一个“古怪”;您的清理处理程序将始终解锁互斥锁,即使您不是持有互斥锁的线程
从手册
调用pthread_mutex_unlock()时,使用调用线程
不保持将导致未定义的行为
您的代码中至少显示一个“古怪”;您的清理处理程序将始终解锁互斥锁,即使您不是持有互斥锁的线程
从手册
调用pthread_mutex_unlock()时,使用调用线程
不保持将导致未定义的行为
代码中的一个更大的问题,可能是死锁的原因,是使用异步取消模式(我以前错过了这个)POSIX中只有3个函数是异步取消安全的:
- pthread_cancel()
- pthread_setcancelstate()
- pthread_setcanceltype()
- 仅在纯计算性的代码中使用它,例如,在没有任何库调用的情况下进行繁重的数学运算,只进行算术运算,或
- 每次打图书馆电话时,都要不断地关闭和打开它
pthread\u cancel
不一定会立即执行任何操作,除非它已在作为取消点的函数(如read
或select
)中被阻止。相反,它将只设置一个标志,下次调用作为取消点的函数时,线程将阻止任何进一步的取消尝试,以推送的相反顺序运行取消清理处理程序,并以指示线程已取消的特殊状态退出
当目标处于异步取消模式时,对其调用pthread\u cancel
将立即中断线程(可能在任何一对相邻的机器代码指令之间)。如果你不明白为什么这是潜在的危险,想一想。任何具有内部状态(静态/全局变量、文件描述符或正在分配/释放的其他资源等)的函数在中断点可能处于不一致的状态:部分修改的变量、已获得一半的锁、已获得但未获得记录的资源,或者被释放,但没有被释放的记录,等等
在异步中断点,进一步的取消请求被阻止,所以从清理处理程序调用任何您喜欢的函数都没有危险。当清理处理程序完成运行时,线程当然就不存在了
另一个潜在的混乱来源:清理处理程序不会与被取消的线程并行运行。当执行取消操作时,被取消的线程停止运行正常的代码流,而是运行清理处理程序,然后退出。您的代码的一个更大的问题,可能是死锁的原因,是您使用异步取消模式(我以前错过了这个)POSIX中只有3个函数是异步取消安全的:
- pthread_cancel()
- pthread_setcancelstate()
- pthread_setcanceltype()
- 仅在纯计算性的代码中使用它,例如,在没有任何库调用的情况下进行繁重的数学运算,只进行算术运算,或
- 每次打图书馆电话时,都要不断地关闭和打开它
pthread\u cancel
不一定会立即执行任何操作,除非它已在作为取消点的函数(如read
或select
)中被阻止。相反,它将只设置一个标志,下次调用作为取消点的函数时,线程将阻止任何进一步的取消尝试,以推送的相反顺序运行取消清理处理程序,并以指示线程已取消的特殊状态退出
当目标处于异步取消模式时,对其调用pthread\u cancel
将立即中断线程(可能在任何一对相邻的机器代码指令之间)。如果你不明白为什么这是潜在的危险,想一想。任何具有内部状态(静态/全局变量、文件描述符或正在分配/释放的其他资源)的函数,
(gdb) bt
#0 0x40000410 in __kernel_vsyscall ()
#1 0x0094aa77 in pthread_join () from /lib/libpthread.so.0
#2 0x08085745 in authDeinit ()
at /users/qixu/src/moja/auth/src//app/libauth/authAPI.c:1562
#3 0x0807e747 in main ()
at /users/qixu/src/moja/auth/src//app/tests/test_session.c:45