2015-03-08 2 views
0

Следующий фрагмент кода записывается для печати четного числа с одним потоком и нечетным числом с другим потоком.Почему этот код pthread просто висит?

#include <stdio.h> 
#include <pthread.h> 
#include <stdlib.h> 

#define LIMIT 100 

int counter; 

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; 

void* function(void *p) 
{ 
    int expected = *(int *)p; 
    while(expected < LIMIT) 
    { 
     pthread_mutex_lock(&mutex1); 
     while (counter != expected); 
      printf("%d\n", counter++); 
     expected += 2; 
     pthread_mutex_unlock(&mutex1); 
    }; 
    exit(0); 
} 

int main(int argc, char *argv[]) 
{ 
    pthread_t thread1, thread2; 
    int counter = 0; 
    int expected_0 = 0, expected_1 = 1; 

    pthread_create(&thread1, NULL, function, (void *)&expected_0); 
    pthread_create(&thread2, NULL, function, (void *)&expected_1); 

    pthread_join(thread1, NULL); 
    pthread_join(thread2, NULL); 

    return 0; 
} 

Но когда выполнить это показывает выход 0 и вешает

[email protected]:~$ gcc -pthread even_odd.c 
[email protected]:~$ ./a.out 
0 

Может ли одна просьба указать мне, что происходит здесь?

Также была бы лучшая логика для выполнения этой задачи?

ответ

1

Это классический случай тупика мьютекса.

Давайте посмотрим детали:

Здесь созданы две нити последовательно, сначала один, ожидающей counter быть 0, а второй, ожидающей counter быть 1.Then каждый будет увеличивать счетчик на единицу и что делать другого потока ожидаемое значение.

Теперь не нужно, что если вы создаете нить thread1 перед тем нити thread2 затем нить thread1 всегда будет выполняться до резьбы thread2. Это полностью зависит от вашего планировщика ОС, и у вас есть минимальный контроль над ним.

Так что, когда thread2 начинает выполняться до thread1, он приобретет mutex1 и начнет ждать в while (counter != expected); заявлении. Теперь, когда thread1 начинает эксцесс, он будет заблокирован mutex1, поскольку он уже получен thread2. Теперь некому обновить counter. Это вызовет тупик.

Вы должны использовать следующий код в качестве функции потока();

void* function(void *p) 
{ 
    int expected = *(int *)p; 
    while(expected < LIMIT) 
    { 
     pthread_mutex_lock(&mutex1); 
     if (counter == expected) { 
      printf("%d\n", counter++); 
      expected += 2; 
     } 
     pthread_mutex_unlock(&mutex1); 
    }; 
} 

Здесь

  • Я блокировки мьютекса всякий раз, когда я хочу, чтобы прочитать или написать глобальную переменную счетчик. Это предотвращает состояние гонки.
  • Я освобождаю мьютекс без занятости, ожидая внутри заблокированных мьютексов для той же переменной. Это предотвращает тупик.
+2

Чтение 'counter' вне блокировки мьютекса - это гонка данных, поэтому неопределенное поведение. –

+1

Не только это, вы оставили ';' после 'while()', поэтому ваши потоки будут входить в бесконечный цикл, обращаясь к 'counter', без мьютекса. –

+0

@Sunil см. Мой ответ, я понял, почему цикл по-прежнему бесконечен, но блокирует мьютекс, прежде чем внутренний цикл while() будет правильным. –

1

У вас есть несколько проблем.

Главное, что когда поток должен ждать counter, чтобы достичь значения expected, он не разблокирует мьютексы, чтобы разрешить другой поток. Это устанавливает, что (добавить <stdbool.h> и скомпилировать в качестве C99):

void* function(void *p) 
{ 
    int expected = *(int *)p; 
    while(expected < LIMIT) 
    { 
     bool my_turn = false; 
     while (!my_turn) 
     { 
      pthread_mutex_lock(&mutex1); 
      my_turn = (counter == expected); 
      pthread_mutex_unlock(&mutex1); 
     } 
     pthread_mutex_lock(&mutex1); 
     printf("%d\n", counter++); 
     pthread_mutex_unlock(&mutex1); 
     expected += 2; 
    }; 

Код выше запирает мьютекс для проверки счетчика, а затем отпирает его снова и петли, если счетчик не достиг ожидаемого значения, поэтому другой поток может работать. Затем он снова блокирует мьютексы, чтобы снова получить доступ к counter.

Другая проблема заключается в вызове exit(0), который вызывает выход обоих потоков, как только один из них заканчивается. Вы, вероятно, хотите, чтобы просто вернуться из функции вместо:

return NULL; 
} 

Другой способ, чтобы написать цикл будет:

 while (true) 
     { 
      pthread_mutex_lock(&mutex1); 
      if (counter == expected) 
       break; 
      pthread_mutex_unlock(&mutex1); 
     } 
     printf("%d\n", counter++); 
     pthread_mutex_unlock(&mutex1); 
     expected += 2; 

Это немного сложнее понять, но избегает отпирание и повторно фиксирования мьютекс, когда счетчик достиг ожидаемого значения. Вместо этого он выходит из цикла, оставляя блокировку мьютекса, распечатывает значение, затем разблокирует мьютекс.

Смежные вопросы