C pthread_join()挂起在压力测试中

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

我正在运行一个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_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