【问题标题】:pthread_cond_wait: random segmentation faultpthread_cond_wait:随机分段错误
【发布时间】:2012-08-05 12:09:36
【问题描述】:

更新 3

最近,我注意到我的代码随机导致 Segmentation Fault 错误。但我认为到目前为止我的代码非常简单,我无法弄清楚该错误来自哪里。由于它是随机发生的,我假设存在某种竞争条件。我认为这是所有可能相关的代码,如果您需要更多,请告诉我:

namespace thread {
    pthread_t terminated_thread_id, /* and others */;
    pthread_mutex_t terminate_thread = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t terminate_thread_signal = PTHREAD_COND_INITIALIZER;
    int total_thread_count = 0;
    int termination; // + sembufs

    inline void* Exit(void* value) {
    //  This must be unlocked after all join-related jobs are done
        semop(thread::termination, thread::termination_in_process, 2)
            pthread_mutex_lock(&thread::terminate_thread);
                thread::terminated_thread_id = pthread_self();
                pthread_cond_signal(&thread::terminate_thread_signal);
            pthread_mutex_unlock(&thread::terminate_thread);

        pthread_exit(value);
        return value;
    }
}
int main(int argc, const char** argv){
...
    pthread_mutex_lock(&thread::terminate_thread);
    if(0 != pthread_create(&thread::communication_handler_thread_id, NULL,    \
                           CommunicationHandler, NULL)){
        global::PrintDebug("pthread_create() failed", __FILE__, __LINE__);
    }
    /** 2 more pthread_create()-calls */       
    do{
        thread::terminated_thread_id = pthread_self();
        pthread_cond_wait(&thread::terminate_thread_signal,                   \
                          &thread::terminate_thread);
        if(!pthread_equal(thread::terminated_thread_id, pthread_self())){
            pthread_join(thread::terminated_thread_id, NULL);
    ...
            semop(thread::termination, thread::termination_done, 1)
        }
    }while(thread::total_thread_count > 0);

    pthread_mutex_unlock(&thread::terminate_thread);
    return 0;
}

terminate_thread_signal 信号仅在 thread::Exit() 函数中发出。该函数也仅在用于创建线程的函数结束时调用。

这是调试器为调用堆栈显示的内容:

#0 (    0xb7fe2424 in __kernel_vsyscall() (??:??)
#1 0xb7fbdfcf   __pthread_cond_wait(cond=0x80539c0, mutex=0x8053998) (pthread_cond_wait.c:153)
#2 0x804a094    main(argc=1, argv=0xbffff9c4) (/home/papergay/SeekYourCar/0.2/Server/main.cpp:121)

我已经知道的是,如果发生错误,那么还没有线程调用 thread::Exit()。 我还使用了一个未命名的命名空间和一些初始化(如果可能相关的话)。 我使用 Code::Blocks 作为 IDE,使用 GCC 作为编译器。

【问题讨论】:

  • 我根本没有读过你的代码,但请记住,MinGW 做了一些神奇而美妙的事情。例如,您的应用程序可能不会在分段错误时崩溃,但下次调用 free 时。我假设这发生在pthread_cond_wait。您确定程序中的其他任何地方都没有分段错误/未初始化的内存吗?
  • 1 开始你的线程数,然后用0 测试它在我看来是假的。另外你的pthread_exit 没用,return 做得很好。
  • 好的,睡了一会儿后,我查看了线程并部分调整了我的代码。我还尝试了你的建议@Zeta。下次调用 free() 时,它似乎不会崩溃 - 如果您正在谈论语义上正确的函数调用。崩溃点保持不变。
  • 除了简单地使用调试器之外,还有其他方法可以找到我的错误吗?

标签: c++ c pthreads


【解决方案1】:

pthread_cond_wait() 允许虚假唤醒,因此您必须在每次唤醒后重新测试条件本身。这可能会导致您的问题 - 如果主线程在设置 thread::terminated_thread_id 之前虚假唤醒,它会将无效的线程 ID 传递给 pthread_join()

您的代码中还有另一个问题 - 无法保证在互斥锁解锁后发出信号的线程将是下一个唤醒的线程,因此两个线程可以快速连续调用 thread::Exit(),主线程直到第二个退出线程解锁互斥锁后才运行。在这种情况下,您永远不会在第一个线程上调用 pthread_join()

这样的事情应该可以解决这些问题:

namespace thread {
    int terminate_thread_set = 0;
    pthread_mutex_t terminate_thread = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t terminate_thread_set_cond = PTHREAD_COND_INITIALIZER;
    pthread_cond_t terminate_thread_unset_cond = PTHREAD_COND_INITIALIZER;

    /* ... */

    inline void Exit(void* value)
    {
        pthread_mutex_lock(&thread::terminate_thread);
        while (thread::terminate_thread_set)
            pthread_cond_wait(&thread::terminate_thread_unset_cond);
        thread::terminated_thread_id = pthread_self();
        thread::terminate_thread_set = 1;
        pthread_cond_signal(&thread::terminate_thread_set_cond);
        pthread_mutex_unlock(&thread::terminate_thread);

        pthread_exit(value);
    }
}

main:

pthread_mutex_lock(&thread::terminate_thread);

/* ... */

while(thread::total_thread_count > 0) {
    while (!thread::terminate_thread_set)
        pthread_cond_wait(&thread::terminate_thread_set_cond, &thread::terminate_thread);
    thread::terminate_thread_set = 0;
    pthread_join(thread::terminated_thread_id, NULL);
    pthread_cond_signal(&thread::terminate_thread_unset_cond);
...
}
pthread_mutex_unlock(&thread::terminate_thread);

当然,这并不是说您没有其他问题。

【讨论】:

  • 这不会导致死锁吗?线程现在不是同时等待两个信号吗?
  • @Papergay:不,退出线程正在等待terminate_thread_set 变为0,主线程正在等待terminate_thread_set 变为1。请注意,除非它等待的条件为假,否则两个线程都不会等待(并且由于互斥锁被持有,条件不能从它下面改变)。
  • 我消除了你提到的两个缺陷。但它并没有解决我的错误......还有其他方法可以找到错误吗?我会调查任何可能相关的事情。提前致谢。
  • @Papergay:你不能在一个线程中锁定一个互斥锁并在另一个线程中解锁它。
  • @Papergay:只有the spec for pthread_mutex_lock() 表示它是未定义的(或者返回错误,如果它是一个错误检查互斥体),因此不需要实现来支持它。
【解决方案2】:

看起来好像您正在从主进程中解锁 termination_in_process 互斥锁 - 即使它已被另一个线程锁定 - 这是未定义的行为。它可能有效,也可能无效。

解决方案可能是使用 FIFO 缓冲区(例如 std::queue,甚至只是 std::vector)并在 Exit() 函数中将终止线程的线程 ID 推送到它,然后发送您的信号,并让主线程通过缓冲区并加入其中的任何线程。

如果Exit() 在您的段错误时未被调用,这不应该是您的问题的原因,但它仍然是您可能想要修复的问题。

【讨论】:

  • 是的,当时我还不知道这种未定义的行为。而且我认为我在 xD 之前改进了我的代码。 pthreads 上的文档并不是我所说的极好,虽然......我会想到别的东西,正如你所说。谢谢。还有一件事,你在哪里读到的,或者你怎么知道这将是未定义的行为?
  • 可以在这里找到例如:linux.die.net/man/3/pthread_mutex_lock - for PTHREAD_MUTEX_DEFAULT:“如果互斥锁未被调用线程锁定,则尝试解锁它会导致未定义的行为。”
【解决方案3】:

这已经很晚了,但我忘记发布它以供将来参考。 这就是我修复它的方法:

我将我的 GCC 编译器从 4.5.X 版本升级到 4.7.X 版本,并将我的内核从 2.6.X 升级到 3.2.X,并通过提供显式构造函数,以允许在没有初始化的情况下进行全局声明。 但我认为升级 GCC 编译器就足够了。

看起来函数的实现不正确。还是内核代码有错误?

【讨论】:

  • 虽然总是有可能,但不太可能。更有可能的是,新编译器生成的代码中的一些细微变化隐藏了您的错误。您应该发布修改后的代码以供审核。
  • 我没有更改处理线程的代码,所以我只能重复自己。正如我在回答中所写,我只更改了与我的线程处理完全无关但我创建了一个全局实例的类的类结构。
  • 那么你的解决方案肯定还是坏了。等待条件变量的标准形式如下:lock(&state_mutex); while (!ready(&state)) { cv.wait(&state_mutex); } alter(&state); unlock(&state_mutex);.
  • 我认为我的解决方案不是假的?如果您仍然认为它是假的,您介意参考我的确切代码吗?将不胜感激!
  • 抱歉,我一定错过了 while 循环。我会说do…while 是不寻常的。它可能导致线程永远等待永远不会发生的情况。不过,我不知道您的代码是否属于这种情况。
猜你喜欢
  • 2022-01-24
  • 1970-01-01
  • 2013-11-02
  • 2015-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-23
  • 2013-12-06
相关资源
最近更新 更多