pthread_cond_signal вызывает тупик - PullRequest
3 голосов
/ 24 ноября 2011

У меня есть программа, которая блокируется, когда один из потоков вызывает pthread_cond_siganl (или трансляцию).Проблема воспроизводима на 100% в основной программе.Я не мог понять, что с ним не так, и, таким образом, извлек фрагмент кода, который вызывает и сигнал вызывается.Однако тупик не может быть воспроизведен с извлеченной проблемой .

Запуск valgrind в основной программе не сообщает о недопустимых операциях чтения / записи или утечек памяти.

Я хочу знать, каковы возможные причины тупика при вызове pthread_cond_signal.

Извлеченный фрагмент следует.

#include <pthread.h>
#include <math.h>
#include <syscall.h>
#include <assert.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

void Task() {
    cerr << syscall(SYS_gettid) << " In Task, sleeping..." << endl;
    sleep(5);
}

pthread_mutex_t lock;
pthread_cond_t cond;
bool doingTheTask= false;

void* func(void* ) { 
    pthread_mutex_lock(&lock);
    if (doingTheTask) {
        cerr << syscall(SYS_gettid) << " wait... " << endl;
        while ( doingTheTask) {//spurious wake-up
            cerr << syscall(SYS_gettid) << " waiting..." << endl ;
            pthread_cond_wait(&cond, &lock);
            cerr << syscall(SYS_gettid) << " woke up!!!" << endl ;
        }
    }
    else {
        cerr << syscall(SYS_gettid) << " My Turn to do the task..." << endl;
        assert( ! doingTheTask );
        doingTheTask= true;
        pthread_mutex_unlock(&lock);
        Task();
        cerr << syscall(SYS_gettid) << " Before trying to acquire lock" << endl;
        pthread_mutex_lock(&lock);
        cerr << syscall(SYS_gettid) << " After acquiring lock" << endl ;
        assert( doingTheTask );
        doingTheTask = false;
        cerr << syscall(SYS_gettid) << " Before broadcast" << endl;
        pthread_cond_broadcast(&cond);
        cerr << syscall(SYS_gettid) << " After broadcast" << endl;
    }
    pthread_mutex_unlock(&lock);
    return NULL;
}


int main() {
    pthread_mutex_init(&lock,NULL);
    pthread_cond_init(&cond,NULL);
    pthread_t thread[2];

    for ( int i = 0 ;  i < 2 ; i ++ ) {
        if (0 != pthread_create(&thread[i], NULL, func, NULL) ) {
            cerr << syscall(SYS_gettid) << " Error creating thread" << endl;
            exit(1);
        }
    } 

    for ( int i = 0 ;  i < 2 ; i ++ ) {
        pthread_join(thread[i],NULL);
    }
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);

    return 0;
}

Единственная важная часть - это функция func.Остальные части просто представлены для компиляции.

Как я уже говорил, проблема не воспроизводима в этой программе .Разница между этим фрагментом и основной программой:

  • В основной программе mutex и condvar являются полями-членами, а функция - методом-членом.
  • Задача выполняет какую-то задачу вместо сна.
  • Несколько потоков могут ждать, и мы должны передавать, а не сигнализировать.Однако взаимоблокировка воспроизводима на 100%, даже когда я использую сигнал и один ожидающий поток.

Проблема, которую я пытаюсь решить с помощью этого фрагмента кода, - это механизм, который выполняет задачу один раз, когда, по крайней мере,это нужно сделать одному из потоков.Но никакие два потока не должны выполнять задачу параллельно, и как только один из них выполняет задачу, другим не нужно это делать.Клиенты этого метода предполагают, что он блокируется до тех пор, пока задача не будет выполнена (поэтому я не могу сразу же вернуться, увидев, что кто-то выполняет задачу).

Обратные следы заблокированных потоков:

#0  __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1  0x00007ffff73e291c in pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:259

и

#0  __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1  0x00007ffff73e30b1 in pthread_cond_signal@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_signal.S:142

pthread_cond_signal deadlocks является аналогичной проблемой.Но похоже, что один из вопросов был повреждением памяти.У меня нет повреждения памяти (говорит valgrind).

Проблема воспроизводится на 100% на двух машинах, на которых я ее тестировал.(Последняя версия ArchLinux и Uubntu 10.04.3).

Ниже приведен пример вывода основной программы.Это снова показывает, что потоки блокируются перед вызовом pthread_cond_wait и pthread_cond_signal.(В первом столбце показаны идентификаторы потоков).

3967    In Task, sleeping...
3967    My Turn to do the task...
3967    In Task, sleeping...
3973    wait...
3973    waiting...
3976    <output from some other thread>
3967    Before trying to acquire lock
3967    After acquiring lock
3967    Before broadcast

Основная программа на C ++.Но я использую части языка C и поэтому избегаю использования тега C ++.

Ответы [ 2 ]

6 голосов
/ 24 ноября 2011

Глупая ошибка.Я уничтожал mutex и condvar перед тем, как выполнить сигнал и ждать.Для воспроизведения просто переместите функции уничтожения до присоединения к потокам в основной функции.

Все еще удивительно, что на обеих моих машинах это дает 100% согласованное (и неправильное) поведение.

0 голосов
/ 24 ноября 2011

Когда мы вызываем pthread_cond_wait (& cond, & lock), блокировка будет снята, а pthread будет ожидать переменную условия. Когда он получает сигнал по условной переменной, он получает блокировку и выходит из pthread_cond_wait (). В вашей программе вы приобрели блокировку мьютекса перед вызовом pthread_cond_broadcast (& cond), поэтому pthread_cond_wait (& cond, & lock) не может взять блокировку при получении сигнала. Я думаю, что это будет причиной тупика.

...