Как заставить поток ждать других? - PullRequest
0 голосов
/ 29 апреля 2020

Таким образом, моя задача - сделать так, чтобы поток № 14 не заканчивался, пока не будут запущены 5 других потоков (включая поток № 14). Моя стратегия заключается в использовании условных переменных для приостановки потока. Когда 5 потоков «загружены», это означает, что когда thread_count равен 5, я освобождаю поток. Вот что я придумал:

typedef struct {
    int i;
    pthread_cond_t *cond;
    pthread_mutex_t *lock;
}thP2_struct;

int thread_count = 1;
int has_T14_started = 0;
int release_T14 = 0;

void *P2_thread_function(void *arg)
{
    thP2_struct *s = (thP2_struct *) arg;

    if (s->i == 14){
        pthread_mutex_lock(s->lock);
        has_T14_started = 1;
        while(release_T14)
            pthread_cond_wait(s->cond, s->lock);
        pthread_mutex_unlock(s->lock);
    }
    else {
        if (has_T14_started){
            pthread_mutex_lock(s->lock);
            thread_count++;
            if (thread_count == 5){
                release_T14 = 1;
                pthread_cond_signal(s->cond);
                has_T14_started = 0;
            }
            pthread_mutex_unlock(s->lock);
        }

    } 

    return NULL;
}

void function(){

    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_t tid[40];
    thP2_struct data[40];

    for(int i = 0; i < 40; i++){
        data[i].i = i + 1;
        data[i].cond = &cond;
        data[i].lock = &lock;
        pthread_create(&tid[i], NULL, P2_thread_function, &data[i]);
    }

    for(int i = 0; i < 40; i++){
        pthread_join(tid[i], NULL);
    }

    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&lock);
}


Это не работает, как я ожидаю. В чем может быть проблема?

1 Ответ

0 голосов
/ 30 апреля 2020

Самой очевидной проблемой является условие вашего ожидания l oop:

        while(release_T14)

Смысл условия обратный: вы хотите дождаться CV в событие, что поток 14 не был освобожден. В противном случае T14 должен быть понятным для продолжения.

Есть также дополнительные проблемы, связанные с поведением других потоков. В частности, они не производят никакого подсчета потоков до того, как поток 14 сообщит о своем запуске. С вашим текущим кодом поток № 14 оба устанавливает has_T14_started и проверяет release_T14 в той же критической области. Поэтому release_T14 будет оставаться 0 до тех пор, пока has_T14_started не станет ненулевым (см. Выше), поэтому ответ на ваш вопрос

Сколько раз поток 14 будет повторять это, пока l oop где он ожидает переменную условия?

равно нет . Поток 14 никогда не будет ждать, потому что (неверное) условие его продолжения гарантированно будет выполнено в самый первый раз, когда поток 14 его тестирует.

Более того, потоки, отличные от # 14, читают общую переменную has_T14_started снаружи защита мьютекса. Поток 14 изменяет эту общую переменную, поэтому чтение имеет неопределенное поведение. Я предполагаю, что ваша идея состоит в том, чтобы избежать блокировки мьютекса, если поток 14 уже был освобожден, но это безнадежная причина, потому что вам нужно обратиться к общим переменным, чтобы определить, был ли поток 14 освобожден (или должен был быть), и вы можете сделать это безопасно только при правильной синхронизации через мьютекс.

Предлагаемые исправления:

  1. Избавьтесь от переменных release_T14 и has_T14_started в целом. Вам не нужен ни один, и они просто запутывают и усложняют вещи. Избавьтесь от оператора if(T14_has_started) и вместо этого выполните его тело безоговорочно.

  2. Объедините две критические области в одну чуть более широкую область, заблокировав мьютекс непосредственно перед if (s->i == 14){ и разблокировав его. сразу после связанного блока else. (Снимите блокировки и разблокировки внутри этого региона.)

  3. Измените условие для потока 14 на

    while (thread_count < 5)
    
  4. Если вы имеете в виду чтобы сохранить счетчик потоков для большего количества целей, чем просто для определения, когда поток 14 может продолжаться, тогда, возможно, вы захотите переместить оператор thread_count++; за пределы блока else сразу после (переустановленного) вызова pthread_mutex_lock(), чтобы поток 14 увеличивает это тоже. Затем вам также может понадобиться немного изменить условие while.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...