LINUX.ORG.RU

Реализовать последовательно-параллельный запуск потоков

 ,


0

3

Тут есть граф запуска потоков, в нем надо выделить три группы потоков: не синхронизированную, синхронизирована мьютексом и синхронизированную семафорами. http://s019.radikal.ru/i633/1706/5c/dfc357ea6270.png Я уже дня три верчу и так, и этак. А чертовы потоки то отваливаются, то идут не в той последовательности. Помогите, пожалуйста. Может вы увидете, где я в глаза долблюсь.

#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <pthread.h>
#include <semaphore.h>

void *thread_function_a(void *arg);
void *thread_function_c(void *arg);
void *thread_function_e(void *arg);
void *thread_function_b(void *arg);
void *thread_function_d(void *arg);
void *thread_function_f(void *arg);
void *thread_function_h(void *arg);
void *thread_function_g(void *arg);
void *thread_function_i(void *arg);
void *thread_function_k(void *arg);
void *thread_function_m(void *arg);
void *thread_function_n(void *arg);

pthread_mutex_t i_mutex;
pthread_mutex_t n_mutex;
pthread_mutex_t k_mutex;
pthread_mutex_t c_mutex;
pthread_mutex_t d_mutex;
pthread_mutex_t h_mutex;
pthread_mutex_t g_mutex;

sem_t semaphore;

pthread_t a_thread;
pthread_t b_thread;
pthread_t c_thread;
pthread_t d_thread;
pthread_t e_thread;
pthread_t f_thread;
pthread_t h_thread;
pthread_t g_thread;
pthread_t i_thread;
pthread_t k_thread;
pthread_t m_thread;
pthread_t n_thread;

int main(int argc, char * argv[])
{
pthread_mutex_lock(&c_mutex);
pthread_mutex_lock(&d_mutex);
pthread_mutex_lock(&h_mutex);
pthread_mutex_lock(&g_mutex);

sem_init(&semaphore, 0, 3);

pthread_create(&a_thread, NULL, thread_function_a, (void*)"a");
pthread_create(&b_thread, NULL, thread_function_b, (void*)"b");
pthread_create(&c_thread, NULL, thread_function_c, (void*)"c");
pthread_create(&d_thread, NULL, thread_function_d, (void*)"d");
pthread_create(&e_thread, NULL, thread_function_e, (void*)"e");
pthread_create(&f_thread, NULL, thread_function_f, (void*)"f");
pthread_create(&h_thread, NULL, thread_function_h, (void*)"h");
pthread_create(&g_thread, NULL, thread_function_g, (void*)"g");
pthread_create(&i_thread, NULL, thread_function_i, (void*)"i");
pthread_create(&k_thread, NULL, thread_function_k, (void*)"k");
pthread_create(&m_thread, NULL, thread_function_m, (void*)"m");
pthread_create(&n_thread, NULL, thread_function_n, (void*)"n");

pthread_join(e_thread,NULL);
pthread_join(d_thread,NULL);
pthread_join(f_thread,NULL);
pthread_join(g_thread,NULL);
pthread_join(h_thread,NULL);
pthread_join(i_thread,NULL);

pthread_mutex_destroy(&c_mutex);
pthread_mutex_destroy(&d_mutex);
pthread_mutex_destroy(&g_mutex);
pthread_mutex_destroy(&h_mutex);

std::cout <<"\n";
return 0;
}


void *thread_function_a(void *arg)
{
int x = 2;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(10);
}
pthread_mutex_unlock(&c_mutex);
return NULL;
} 

void *thread_function_b(void *arg)
{
pthread_join(a_thread,NULL);
int x =3;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
pthread_mutex_unlock(&k_mutex);
return NULL;
}

void *thread_function_c(void *arg)
{
pthread_mutex_lock(&c_mutex); 
int x = 7;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
return NULL;
}

void *thread_function_e(void *arg)
{
pthread_join(b_thread,NULL);
int x =4;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
pthread_mutex_unlock(&k_mutex);
return NULL;
}

void *thread_function_d(void *arg)
{
pthread_mutex_lock(&d_mutex);
int x = 7;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
return NULL;
}

void *thread_function_f(void *arg)
{
pthread_join(b_thread,NULL);
int x =3;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
pthread_mutex_unlock(&h_mutex);
return NULL;
}


void *thread_function_k(void *arg)
{
pthread_mutex_lock(&k_mutex);
sem_wait(&semaphore);
int x = 5;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
sem_post(&semaphore);
pthread_mutex_unlock(&i_mutex);
return NULL;
}

void *thread_function_i(void *arg)
{
pthread_join(k_thread,NULL);
pthread_join(d_thread,NULL);
pthread_join(g_thread,NULL);
sem_wait(&semaphore);
int x = 4;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
sem_post(&semaphore);
return NULL;
}

void *thread_function_m(void *arg)
{
pthread_join(e_thread,NULL);
sem_wait(&semaphore);
int x = 6;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
sem_post(&semaphore);
pthread_mutex_unlock(&n_mutex);
return NULL;
}

void *thread_function_g(void *arg)
{
pthread_join(f_thread,NULL);
sem_wait(&semaphore);
int x = 4;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
sem_post(&semaphore);
return NULL;
}

void *thread_function_h(void *arg)
{
pthread_mutex_lock(&h_mutex);
sem_wait(&semaphore);
int x = 7;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
sem_post(&semaphore);
pthread_mutex_unlock(&n_mutex);
return NULL;
}


void *thread_function_n(void *arg)
{
pthread_join(i_thread,NULL);
pthread_join(m_thread,NULL);
pthread_join(h_thread,NULL);

int x = 2;
for(int i = 0; i<x;i++){
std::cout <<(char*)arg;
usleep(100);
}
return NULL;}

Вопрос вроде по c++. Почему бы тогда не использовать стандартные средства std::thread std::async?

samson ★★
()
Ответ на: комментарий от Gold_Oldmen

Вообще это не правильно, если говорить о c++. Не просто же так в стандарт добавляли поддержку многопоточности.

samson ★★
()
Ответ на: комментарий от Gold_Oldmen

У вас задача СИНХРОНИЗАЦИИ потоков! Для синхронизации обычно используются condition variables. Для совместного доступа к данным - mutex.

Нужно создать все нити в main() и далее синхронизировать их, с помощью этих примитив синхронизации.

На вашей картинке есть точки «пересечения линий-нитей». Вот вам и надо стартонуть все нити и в каждом thread ожидать, когда та или-иная нить «дойдет» до этой точки (выполнит какую-либо работу). И как только это произошло, продолжать выполннение соответствущего потока/ов.

Те, у вас есть точка X1 где начинается выполнение thread_c (см картинку). Нити C и В должны ожидать (cv_wait) пока нить А не выполнит свою работу и не подаст сигнал об этом. Как только придет сигнал, нити С и В начнут свое выполнение.

Следующая точка пересечения (Х2) - пересечение нитей BFDE - это точка, где нить B выполнит свою работу и подаст сигнал остальным нитям. Нити E, D и F ждут сигнала X2 и начинают свое выполнение по его приходу.

После завершения нити F и С надо подать сигнал X3 и т.д. Тут поток F должен знать что поток С завершился, или поток С должен знать что завершился поток F. Это зависит от того, кто завершится раньше. Здесь можно использовать, например, атомарный флаг, либо флаг под мьютексом.

В main() надо будет дождаться пока нить N (либо все нити) не завершится, потом завержать main. Тут thread_join делать не надо. Проще сделать thread_detach.

Можно использовать и симафоры, и мьютексы, раз от вас того требуют. Смысл тот же.

Можно попробовать запускать некоторые thread из точек X1, X2, X3,..

thread_join дожидается завершения нити, thread_detach нет.

Еще нужно учесть spurious wakeup (ложное пробуждение).

И тат как у вас куча мьтексов, обратите внимание на deadlocks.

Зачем вы понавтыкали useep()? Это не к чему.

ps: В стандарте c11 тоже есть поддержка мультипоточности - thread.h. Поэтому правильнее было бы это и использовать, вмсето posix threads.

pps: картинки выкладывайте на каких-нибудь нормальных post image ресурсах. А то тут пока откроешь эту картинку, рекламой за**т.

ppps: https://ru.wikipedia.org/wiki/Отступ_(программирование)

samson ★★
()
Ответ на: комментарий от samson

ps: В стандарте c11 тоже есть поддержка мультипоточности - thread.h.

В обычном линуксе нет этой поддержки, к сожалению. Нужно брать дистрибутив с библиотекой Musl... glibc забили на стандарты :(

fsb4000 ★★★★★
()

Ошибки по вашему коду правктически в первой же точке:

У вас в function_e и function_f одновременно ожидается thread_join(b_thread)

Joining with a thread that has previously been joined results in undefined behavior.

http://man7.org/linux/man-pages/man3/pthread_join.3.html

Сами подумайте что происходит.

samson ★★
()
Ответ на: комментарий от fsb4000

Да без разницы абсолютно тут, что использовать. Тут отличий то и не будет что thread.h, что pthread.

Друге дело, что в теме тег c++ и раз уж c++, то надо писать на нем, тогда std::async, std::thread итд...

samson ★★
()
Ответ на: комментарий от samson

ИМХО, с помощью usleep() он изображает работу потока, и потом по выводу тип ″bcbcd″ как-то определяет, работали ли потоки одновременно.

Хотя я не знаю, насколько это хорошая идея, делать ″std::cout <<″ из разных потоков.
Я бы в такой код воткнул ″write(1,1,(char*)arg);″.

mky ★★★★★
()
Ответ на: комментарий от samson

cout - thread safe

Нет

Concurrent access to a synchronized (§27.5.3.4) standard iostream object’s formatted and unformatted input (§27.7.2.1) and output (§27.7.3.1) functions or a standard C stream by multiple threads shall not result in a data race (§1.10). [ Note: Users must still synchronize concurrent use of these objects and streams by multiple threads if they wish to avoid interleaved characters. — end note ]

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

под мьютексом надо детать если cout << "var = " << var << endl;. Если же cout << "big string \n"; - мьютекс не нужен.

Запустите следующий код:

// g++ -std=c++11 -lpthread  cppCoutThreads.cpp

#include <iostream>
#include <string>
#include <thread>

void handler() {
  while (true) {
    std::string s("true line: ");
    std::cout << "bad line: thread id = " << std::this_thread::get_id() << "  counter...";
    for (int i=0; i<100; i++) { 
      std::cout  << i%10;
      s += std::to_string(i%10);
    }
    std::cout << std::endl;
    std::cout << "\n" + s + "\n";
  }
}

int main() {
  std::thread t1(handler);
  std::thread t2(handler);
  t1.join();
  t2.join();
  return 0;
}

samson ★★
()
Ответ на: комментарий от fsb4000

Но вы правы. Стандарт разрешает одновременную запись из разных потоков, но вывод может смешиваться.

samson ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.