Синхронизация вывода потоков POSIX

131
24 января 2020, 02:00

Нужно что бы два потока параллельно печатали на экран. (Первый поток печатает числа 1,2,3...10 Второй - 100,200,300...1000). Причём вывод должен быть синхронизирован: сначала родительский поток выводит первую строку, затем дочерний первую, затем родительский вторую строку, затем дочерний вторую и т.д.(100,1,200,2,300,3...) Использовать нужно мьютексы.

pthread_mutex_t mut;
void printt(int i){
   pthread_mutex_lock(&mut);
   cout<<i<<endl;
   pthread_mutex_unlock(&mut);
}
void* func(void*){
   for(int i=1;i<=10;i++){
        printt(i);
   }
   return 0;
}
int main() {
   pthread_mutex_init(&mut,0);
   pthread_t tid;
   pthread_create(&tid,0,func,0);
   for(int i=100;i<=1000;i+=100){
        printt(i);
   }
   pthread_join(tid,0);
   pthread_mutex_destroy(&mut);
   return 0;
}

Мой код же печатает сначала main поток 100,200... а потом 1,2... Хотя во время отладки программы печатает, как сказано в задании.

Answer 1

Предупреждение: согласно POSIX данное решение даёт UB [1], хотя и, судя по всему, он работает в реализации pthreads от glibc/linux; он приведен лишь для справки/как идея и не должен использоваться. Спасибо @VTT за замечание.

Для решения задачи нужно количество мьютексов равное количеству потоков.

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

pthread_mutex_t mut1;
pthread_mutex_t mut2;
void printt1(int i){
   pthread_mutex_lock(&mut1);
   cout<<i<<endl;
   pthread_mutex_unlock(&mut2);
}
void printt2(int i){
   pthread_mutex_lock(&mut2);
   cout<<i<<endl;
   pthread_mutex_unlock(&mut1);
}
void* func(void*){
   for(int i=1;i<=10;i++){
        printt2(i);
   }
   return 0;
}
int main() {
   pthread_mutex_init(&mut1,0);
   pthread_mutex_init(&mut2,0);
   pthread_mutex_lock(&mut2);
   pthread_t tid;
   pthread_create(&tid,0,func,0);
   for(int i=100;i<=1000;i+=100){
        printt1(i);
   }
   pthread_join(tid,0);
   pthread_mutex_destroy(&mut1);
   pthread_mutex_destroy(&mut2);
   return 0;
}
Answer 2

Функция printt печатает локальную переменную i, к которой у других потоков нет доступа, а синхронизация не ожидает завершения записи предыдущей строки другим потоком. Также полностью отсутствует обработка ошибок.

Answer 3

Без сигналов , чтоб другой поток проснулся неудобно, придумал только сон. Вроде бы пашет. Нужна переменная для знака кому какая очередь.

// g++ -pthread mutex-semaph.cpp
#include <unistd.h>
# include <pthread.h>
# include <iostream>
pthread_mutex_t mut;
int queue = 1 ; // или 2
void printt(int i, int q){
Again   :
    pthread_mutex_lock(&mut);
        if(queue == q)   std::cout<<i<<std::endl;
        else    {
            pthread_mutex_unlock(&mut);
            sleep(1);
            goto    Again   ;   }
        queue = 3 - q ;
   pthread_mutex_unlock(&mut);
}
void* func(void*){
   for(int i=1;i<=10;i++){
        printt(i,2);
   }
   return 0;
}
int main() {
   pthread_mutex_init(&mut,0);
   pthread_t tid;
   pthread_create(&tid,0,func,0);
   for(int i=100;i<=1000;i+=100){
        printt(i,1);
   }
   pthread_join(tid,0);
   pthread_mutex_destroy(&mut);
   return 0;
}
Answer 4

Сделайте разделяемую volatile переменную и mutex. Присвойте переменной 1, что означает печать будет проводить первый поток. Запустите потоки.

В каждом потоке в цикле захватываете mutex и читаете значение переменной.

Если значение переменной в первом потоке равно 1, то он печатает данные и присваивает переменной 2. Аналогично, второй поток производит печать если значение переменной равно 2, после чего устанавливает ее в 1.
(Обратите внимание, чтение и модификация переменной защищены mutex-ом.)

Затем поток в любом случае снимает блокировку и (для оптимизации эффективности) вызывает pthread_yield.

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

Впрочем, хватит общих слов. Вот немного модифицированный ваш код из текста вопроса.

avp@avp-ubu1:hashcode$ cat t-seq-pri.c
#ifdef __cplusplus
#include <iostream>
using namespace std;
#else
#define _GNU_SOURCE
#include <stdio.h>
#endif
#include <pthread.h>
pthread_mutex_t mut;
volatile int turn;
void printt(int i, int q){
  int pri = 0;
  do {
    pthread_mutex_lock(&mut);
    if (turn == q) {
#ifdef __cplusplus      
      cout<<i<<endl;
#else
      printf("%d\n", i);
#endif
      turn = (q == 1 ? 2 : 1);
      pri = 1;
    }
    pthread_mutex_unlock(&mut);
    pthread_yield();
  } while (!pri);
}
void* func(void *p){
  for(int i=1;i<=10;i++){
    printt(i, 2);
   }
   return 0;
}
int main() {
   pthread_mutex_init(&mut,0);
   pthread_t tid;
   turn = 1;
   pthread_create(&tid,0,func,0);

   for(int i=100;i<=1000;i+=100){
     printt(i, 1);
   }
   pthread_join(tid,0);
   pthread_mutex_destroy(&mut);
   return 0;
}

avp@avp-ubu1:hashcode$ gcc t-seq-pri.c -pthread -Wall -O3 && ./a.out
100
1
200
2
300
3
400
4
500
5
600
6
700
7
800
8
900
9
1000
10
avp@avp-ubu1:hashcode$ 
READ ALSO
как с вывести один элемент пары класса multimap?

как с вывести один элемент пары класса multimap?

Допустим у меня есть следующий код:

106
(c++)OpenGL и glfw - приключения на мою голову

(c++)OpenGL и glfw - приключения на мою голову

еееееПроблемы начались СРАЗУ

125
Стек exception&#39;ов, как обработать все?

Стек exception'ов, как обработать все?

Вопрос на засыпку, кто знает, можно ли перехватить MyException1 в func3?

123
Не полчается пройти авторизацию через Jsoup

Не полчается пройти авторизацию через Jsoup

Не могу авторизоваться с помощью Jsoup для дальнейшего парсингаВ чем проблема можете подсказать? Ниже предоставлен код и HTTP запрос

96