При вызове pthread_cond_signal в программе с 4 потоками, тот же поток получает мьютекс - PullRequest
0 голосов
/ 05 октября 2018
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_t node[4];
pthread_mutex_t token;
pthread_cond_t cond;
int id=0;

void *func(int n)
   {
        int count = 0;
        while (count < 10){
                 pthread_mutex_lock(&token);
                while (id != n){
                        printf("Whoops not my turn, id=%d\n",n);
                        pthread_cond_wait(&cond, &token);}
                //if (id == n){
                        count += 1;
                        printf ("My turn! id= %d\n",n);
                        printf("count %d\n", count);
                        if (id == 3){
                                id = 0;}
                        else{
                                id += 1;}
                        //}else{
                        //      printf("Not my turn! id=%d\n",n);}
                        //      pthread_mutex_unlock(&token);
                        //      sleep(2);}
                        pthread_mutex_unlock(&token);
                        pthread_cond_signal(&cond);}
                        printf ("ID=%d has finished\n",n);

        return(NULL);
   }

int main()
   {
   int i;
   pthread_mutex_init(&token,NULL);
        pthread_cond_init(&cond,NULL);
   for(i=0;i<4;i++)
      pthread_create(&node[i],NULL,(void *)func,(void *)i);

   for(i=0;i<4;i++)
      pthread_join(node[i],NULL);

   pthread_mutex_destroy(&token);

   return 0;
   }

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

Скажем, поток с идентификатором 1 (определенным в func n) получает мьютекс и возвращает "Мой ход! Id = 1 ".Когда вызваны mutex_unlock и cond_signal, следующим потоком, который получит мьютекс, будет снова поток с идентификатором 1, и он выведет «Whoops not my turn, id = 1».И только тогда поток с идентификатором 2 получит мьютекс и напечатает «Мой ход! Id = 2», но после этого поток с идентификатором 2 получит мьютекс.Вот мой вывод программы:

Whoops not my turn, id=1
Whoops not my turn, id=2
Whoops not my turn, id=3
My turn! id= 0
count 1
Whoops not my turn, id=0
My turn! id= 1
count 1
Whoops not my turn, id=1
My turn! id= 2
count 1
Whoops not my turn, id=2
My turn! id= 3
count 1
Whoops not my turn, id=3
My turn! id= 0
count 2
Whoops not my turn, id=0
My turn! id= 1
count 2
Whoops not my turn, id=1
My turn! id= 2
count 2
Whoops not my turn, id=2
My turn! id= 3
count 2
Whoops not my turn, id=3
My turn! id= 0
count 3
Whoops not my turn, id=0
My turn! id= 1
count 3
Whoops not my turn, id=1
My turn! id= 2
count 3
Whoops not my turn, id=2
My turn! id= 3
count 3
Whoops not my turn, id=3
My turn! id= 0
count 4
Whoops not my turn, id=0
My turn! id= 1
count 4
Whoops not my turn, id=1
My turn! id= 2
count 4
Whoops not my turn, id=2
My turn! id= 3
count 4
Whoops not my turn, id=3
My turn! id= 0
count 5
Whoops not my turn, id=0
My turn! id= 1
count 5
Whoops not my turn, id=1
My turn! id= 2
count 5
Whoops not my turn, id=2
My turn! id= 3
count 5
Whoops not my turn, id=3
My turn! id= 0
count 6
Whoops not my turn, id=0
My turn! id= 1
count 6
Whoops not my turn, id=1
My turn! id= 2
count 6
Whoops not my turn, id=2
My turn! id= 3
count 6
Whoops not my turn, id=3
My turn! id= 0
count 7
Whoops not my turn, id=0
My turn! id= 1
count 7
Whoops not my turn, id=1
My turn! id= 2
count 7
Whoops not my turn, id=2
My turn! id= 3
count 7
Whoops not my turn, id=3
My turn! id= 0
count 8
Whoops not my turn, id=0
My turn! id= 1
count 8
Whoops not my turn, id=1
My turn! id= 2
count 8
Whoops not my turn, id=2
My turn! id= 3
count 8
Whoops not my turn, id=3
My turn! id= 0
count 9
Whoops not my turn, id=0
My turn! id= 1
count 9
Whoops not my turn, id=1
My turn! id= 2
count 9
Whoops not my turn, id=2
My turn! id= 3
count 9
Whoops not my turn, id=3
My turn! id= 0
count 10
ID=0 has finished
My turn! id= 1
count 10
ID=1 has finished
My turn! id= 2
count 10
ID=2 has finished
My turn! id= 3
count 10
ID=3 has finished

Как видите, после каждого успеха поток выводит «Мой ход!», После этого он получает мьютекс и вызывает «К сожалению, не мой ход!».Я не понимаю, почему это происходит, когда я вызываю pthread_cond_signal, который должен дать сигнал другому потоку проснуться, прежде чем текущий поток сможет восстановить мьютекс.Пожалуйста, помогите мне найти это решение, так как я думаю, что я упускаю что-то важное.Если мое объяснение отсутствует, пожалуйста, не стесняйтесь спрашивать меня для получения дополнительной информации.Большое спасибо за ваше время!

Ответы [ 2 ]

0 голосов
/ 05 октября 2018

@ rcgldr уже дал очень хорошее объяснение о сроках.Если вы хотите увеличить свои шансы на то, чтобы дать другому потоку шанс, попробуйте добавить вызов к pthread_yield , который должен дать планировщику возможность выбрать другой поток, хотя это тоже не будет гарантией.

0 голосов
/ 05 октября 2018

В случае Linux и, возможно, систем posix в целом, нет никакой гарантии порядка обслуживания запросов блокировки мьютекса.Когда мьютекс разблокирован, ОС не вызывает переключение контекста, поэтому текущий запущенный поток продолжает цикл и снова блокирует мьютекс.Я не знаю, можете ли вы изменить приоритет потока через интерфейс pthread, но если это возможно, вы можете просто увеличить приоритет потока "(n)% 4", и когда поток "(n)% 4" будет запущен,он установил бы свой приоритет на нормальный и установил бы для потока "(n + 1)% 4" более высокий приоритет.

В случае Windows, использующей свой собственный мьютекс, очевидно, порядок запросов на блокировку отслеживается вочередь или эквивалент, поэтому, когда текущий работающий поток возвращается к запросу блокировки, Windows переключится на первый поток в очереди запросов блокировки для мьютекса.Я не знаю, если это задокументировано, но я подтвердил, что это работает таким образом.Я не знаю, будет ли таким образом работать мьютекс pthreads в Windows.

Возможная альтернатива - один мьютекс на поток, но я не знаю, может ли это вызвать какие-либо проблемы в зависимости от ОС.Использование одного семафора на поток должно работать

Примечание: если вы используете переменную условия, вы можете получить ложное пробуждение, и вам нужно будет справиться с этой ситуацией.

https://en.wikipedia.org/wiki/Spurious_wakeup

Однако, если использовать собственные типы синхронизации Windows, такие как мьютекс, семафор, ..., ложных пробуждений не произойдет.

Для некоторых ОС, таких как Linux, этих проблем достаточно, чтобы некоторые издля завершения многопоточных / многопоточных приложений установите драйвер уровня ядра, чтобы реализовать временные блокировки ядра, чтобы избежать этих проблем.

https://en.wikipedia.org/wiki/Spinlock

...