Многопоточность - продолжение только после того, как ВСЕ потоки завершили задачу - PullRequest
2 голосов
/ 13 апреля 2019

я пытаюсь реализовать многопоточность с многопоточными потоками (пользователь может ввести число рабочих = потоков при запуске программы) где каждый поток вызывает функцию A, а затем функцию B. Но перед функцией B следует выполнять только после ВСЕХ потоков вызвали функцию. Вот мой псевдокод:

void* worker_do(void* worker_id)
{
  functionA((size_t) worker_id);
  // First thread should only start functionB after ALL threads
  // are finished with functionA
  functionB((size_t) worker_id);
  return NULL;
}

// I am not allowed to change pthread_create and pthread_join here
int main()
{
  // should be a flexible value
  ssize_t num_workers = 20;
  pthread_t* workers  = malloc(num_workers*sizeof(pthread_t));

  for(ssize_t i = 0; i < num_workers; i++)
    pthread_create(&workers[i], NULL, worker_do, (void*) i);

  for(ssize_t i = 0; i < num_workers; i++)
    pthread_join(workers[i], NULL);

  free(workers);

  return 0;
}

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

IF last_thread_has_called_functionA THEN start_calling_fuctionB

Или переменные условия не подходят для решения этой проблемы?

Буду очень признателен, как я могу это реализовать ...

bw Robert

Ответы [ 2 ]

1 голос
/ 13 апреля 2019

Я предполагаю, что functionA() и functionB() могут выполняться параллельно потоками, поскольку в вашем текущем коде нет защиты мьютекса.

Для решения вашей проблемы вы можете использовать простой механизм опроса.После выполнения функции A () каждый поток будет увеличивать счетчик.Все потоки будут ждать, пока счетчик не станет равным количеству созданных потоков.

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

unsigned int num_threads = 0;
unsigned int num_threads_completed_functionA = 0;
pthread_mutex_t lock;

void* worker_do(void* worker_id)
{
  functionA((size_t) worker_id);
  // First thread should only start functionB after ALL threads are finished with functionA

  //Lock the mutex and update the counter
  pthread_mutex_lock(&lock);
  num_threads_completed_functionA++;
  pthread_mutex_unlock(&lock);

  while(1)
  {
    //Lock mutex and check how many threads completed execution of functionA()
    pthread_mutex_lock(&lock);
    if(num_threads_completed_functionA == num_threads)
    {
       //If all threads completed, then break the loop and proceed executing functionB()
       pthread_mutex_unlock(&lock);
       break;
    }
    pthread_mutex_unlock(&lock);
    usleep(1); //Sleep for some time
  }

  //ALL threads are finished with functionA
  functionB((size_t) worker_id);
  return NULL;
}
0 голосов
/ 14 апреля 2019

Поскольку вы попросили сделать это с условной переменной и мьютексом, вы можете сделать что-то вроде этого:

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

#define N_THREADS   10

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
unsigned int count = 0;

void functionA(intptr_t id)
{
    printf("functionA: %" PRIdPTR "\n", id);
}

void functionB(intptr_t id)
{
    printf("functionB: %" PRIdPTR "\n", id);
}

void* thread_proc(void* pv)
{
    intptr_t id = (intptr_t)pv;

    functionA(id);

    // lock the mutex to protect the predicate data (count)
    pthread_mutex_lock(&mtx);
    ++count;
    pthread_cond_broadcast(&cv);

    // wait for all threads to finish A
    while (count < N_THREADS)
        pthread_cond_wait(&cv, &mtx);

    // this is still owned by us. release it.
    pthread_mutex_unlock(&mtx);

    // now B
    functionB(id);

    return NULL;
}

int main()
{
    pthread_t thrds[N_THREADS];
    for (int i=0; i<N_THREADS; ++i)
        pthread_create(thrds+i, NULL, thread_proc, (void*)(intptr_t)(i+1));

    for (int i=0; i<N_THREADS; ++i)
        pthread_join(thrds[i], NULL);

    return EXIT_SUCCESS;
}

Пример вывода (варьируется)

functionA: 1
functionA: 4
functionA: 6
functionA: 3
functionA: 2
functionA: 8
functionA: 9
functionA: 7
functionA: 10
functionA: 5
functionB: 10
functionB: 9
functionB: 5
functionB: 7
functionB: 4
functionB: 6
functionB: 1
functionB: 2
functionB: 8
functionB: 3

Тем не менее, как отметил Джонатан в общем комментарии, барьер является более элегантным решением этой проблемы. Я бы опубликовал пример, но, увы, моя среда их не поддерживает (грустно, mac os x). Они доступны в большинстве реализаций Unix pthread, поэтому, если ваша целевая платформа предоставляет их, я предлагаю изучить их надлежащим образом.

...