Я пытаюсь написать код, который не блокирует main() при вызове pthread_join(): то есть в основном пытаюсь реализовать мой предыдущий вопрос, упомянутый ниже:

https://stackoverflow.com/questions/24509500/pthread-join-and-main-blocking-multithreading

И соответствующее объяснение по адресу:

pthreads - присоединиться к группе потоков, дождаться выхода одного из них

Согласно предложенному ответу:

You'd need to create your own version of it - e.g. an array of flags (one flag per thread) protected by a mutex and a condition variable; where just before "pthread_exit()" each thread acquires the mutex, sets its flag, then does "pthread_cond_signal()". The main thread waits for the signal, then checks the array of flags to determine which thread/s to join (there may be more than one thread to join by then).

Я пробовал, как показано ниже:

Мой массив статуса, который отслеживает, какие потоки завершились:

typedef struct {
    int Finish_Status[THREAD_NUM];
    int signalled;

    pthread_mutex_t mutex;
    pthread_cond_t FINISHED;
    }THREAD_FINISH_STATE;

Подпрограмма потока устанавливает соответствующий элемент массива, когда поток завершается, а также сигнализирует переменную условия:

void* THREAD_ROUTINE(void* arg)
{
    THREAD_ARGUMENT* temp=(THREAD_ARGUMENT*) arg;
    printf("Thread created with id %d\n",temp->id);
    waitFor(5);
    pthread_mutex_lock(&(ThreadFinishStatus.mutex));
    ThreadFinishStatus.Finish_Status[temp->id]=TRUE;
    ThreadFinishStatus.signalled=TRUE;
    if(ThreadFinishStatus.signalled==TRUE)
    {
      pthread_cond_signal(&(ThreadFinishStatus.FINISHED));
      printf("Signal that thread %d finished\n",temp->id);
     }
    pthread_mutex_unlock(&(ThreadFinishStatus.mutex));

    pthread_exit((void*)(temp->id));
    }

Я не могу написать соответствующие части функций pthread_join() и pthread_cond_wait(). Есть несколько вещей, которые я не могу реализовать.

1) Как записать соответствующую часть pthread_cond_wait() в мой main()?

2) Я пытаюсь написать это как:

   pthread_mutex_lock(&(ThreadFinishStatus.mutex));
    while((ThreadFinishStatus.signalled != TRUE){
     pthread_cond_wait(&(ThreadFinishStatus.FINISHED), &(ThreadFinishStatus.mutex));
     printf("Main Thread signalled\n");
     ThreadFinishStatus.signalled==FALSE; //Reset signalled
     //check which thread to join
    }
    pthread_mutex_unlock(&(ThreadFinishStatus.mutex)); 

Но он не входит в цикл while.

3) Как использовать pthread_join(), чтобы получить возвращаемое значение, хранящееся в моем arg[i].returnStatus то есть, где поставить нижеприведенный оператор в моем основном:

`pthread_join(T[i],&(arg[i].returnStatus));`

ПОЛНЫЙ КОД

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

#define THREAD_NUM 5
#define FALSE 0
#define TRUE 1


void waitFor (unsigned int secs) {
    time_t retTime;
    retTime = time(0) + secs;     // Get finishing time.
    while (time(0) < retTime);    // Loop until it arrives.
}

typedef struct {
    int Finish_Status[THREAD_NUM];
    int signalled;

    pthread_mutex_t mutex;
    pthread_cond_t FINISHED;
    }THREAD_FINISH_STATE;

typedef struct {
    int id;
    void* returnStatus;
    }THREAD_ARGUMENT;

THREAD_FINISH_STATE ThreadFinishStatus;

void initializeState(THREAD_FINISH_STATE* state)
{
 int i=0;
 state->signalled=FALSE;
 for(i=0;i<THREAD_NUM;i++)
 {
     state->Finish_Status[i]=FALSE;
     }
 pthread_mutex_init(&(state->mutex),NULL);
 pthread_cond_init(&(state->FINISHED),NULL);
    }

void destroyState(THREAD_FINISH_STATE* state)
{
 int i=0;
 for(i=0;i<THREAD_NUM;i++)
 {
     state->Finish_Status[i]=FALSE;
     }
 pthread_mutex_destroy(&(state->mutex));
 pthread_cond_destroy(&(state->FINISHED));
    }


void* THREAD_ROUTINE(void* arg)
{
    THREAD_ARGUMENT* temp=(THREAD_ARGUMENT*) arg;
    printf("Thread created with id %d\n",temp->id);
    waitFor(5);
    pthread_mutex_lock(&(ThreadFinishStatus.mutex));
    ThreadFinishStatus.Finish_Status[temp->id]=TRUE;
    ThreadFinishStatus.signalled=TRUE;
    if(ThreadFinishStatus.signalled==TRUE)
    {
      pthread_cond_signal(&(ThreadFinishStatus.FINISHED));
      printf("Signal that thread %d finished\n",temp->id);
     }
    pthread_mutex_unlock(&(ThreadFinishStatus.mutex));

    pthread_exit((void*)(temp->id));
    }

int main()
{
    THREAD_ARGUMENT arg[THREAD_NUM];
    pthread_t T[THREAD_NUM];
    int i=0;
    initializeState(&ThreadFinishStatus);

    for(i=0;i<THREAD_NUM;i++)
    {
        arg[i].id=i;
        }

    for(i=0;i<THREAD_NUM;i++)
        {
            pthread_create(&T[i],NULL,THREAD_ROUTINE,(void*)&arg[i]);
        }

    /*
     Join only if signal received
    */

    pthread_mutex_lock(&(ThreadFinishStatus.mutex));
    //Wait
    while((ThreadFinishStatus.signalled != TRUE){
     pthread_cond_wait(&(ThreadFinishStatus.FINISHED), &(ThreadFinishStatus.mutex));
     printf("Main Thread signalled\n");
     ThreadFinishStatus.signalled==FALSE; //Reset signalled
     //check which thread to join
    }
    pthread_mutex_unlock(&(ThreadFinishStatus.mutex));

    destroyState(&ThreadFinishStatus);
    return 0;
    }
3
Gaurav K 1 Июл 2014 в 23:02

4 ответа

Лучший ответ

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

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

Самое главное, что эта программа не тупиковая и не гонится.

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

void* ThreadEntry(void* args );

typedef struct {
    int threadId;
    pthread_t thread;
    int threadResult;
} ThreadState;

sem_t           completionSema;

pthread_mutex_t resultMutex;

std::queue<int> threadCompletions;

ThreadState* threadInfos;

int main() {

    int numThreads = 10;
    int* threadResults;
    void* threadResult;
    int doneThreadId;


    sem_init( &completionSema, 0, 0 );

    pthread_mutex_init( &resultMutex, 0 );

    threadInfos = new ThreadState[numThreads];

    for ( int i = 0; i < numThreads; i++ ) {

        threadInfos[i].threadId = i;
        pthread_create( &threadInfos[i].thread, NULL, &ThreadEntry, &threadInfos[i].threadId  );
    }

    for ( int i = 0; i < numThreads; i++ ) {
        // Wait for any one thread to complete; ie, wait for someone
        // to queue to the threadCompletions queue.
        sem_wait( &completionSema );


        // Find out what was queued; queue is accessed from multiple threads,
        // so protect with a vanilla mutex.
        pthread_mutex_lock(&resultMutex);
        doneThreadId = threadCompletions.front();
        threadCompletions.pop();
        pthread_mutex_unlock(&resultMutex);

        // Announce which thread ID we saw finish
        printf(
            "Main saw TID %d finish\n\tThe thread's result was %d\n",
            doneThreadId,
            threadInfos[doneThreadId].threadResult
        );

        // pthread_join to clean up the thread.
        pthread_join( threadInfos[doneThreadId].thread, &threadResult );
    }

    delete threadInfos;

    pthread_mutex_destroy( &resultMutex );
    sem_destroy( &completionSema );

}

void* ThreadEntry(void* args ) {
    int threadId = *((int*)args);

    printf("hello from thread %d\n", threadId );

    // This can safely be accessed since each thread has its own space
    // and array derefs are thread safe.
    threadInfos[threadId].threadResult = rand() % 1000;


    pthread_mutex_lock( &resultMutex );
    threadCompletions.push( threadId );
    pthread_mutex_unlock( &resultMutex );

    sem_post( &completionSema );

    return 0;
}
3
antiduh 1 Июл 2014 в 20:45

Условия Pthread не имеют «памяти»; pthread_cond_wait не возвращается, если pthread_cond_signal вызывается до pthread_cond_wait, поэтому важно проверять предикат перед вызовом pthread_cond_wait и не вызывать его, если он истинен. Но это означает, что действие, в данном случае «проверить, к какому потоку присоединиться», должно зависеть только от предиката, а не от того, вызывается ли pthread_cond_wait.

Кроме того, вы можете сделать так, чтобы цикл while фактически ждал завершения всех потоков, чего вы сейчас не делаете.

(Кроме того, я думаю, что другой ответ о том, что «signalaled == FALSE» безвреден, неверен, он небезопасен, потому что есть pthread_cond_wait, и когда он вернется, signaled изменится на true.)

Поэтому, если бы я хотел написать программу, ожидающую завершения всех потоков таким образом, это было бы больше похоже на

pthread_mutex_lock(&(ThreadFinishStatus.mutex));
// AllThreadsFinished would check that all of Finish_Status[] is true
// or something, or simpler, count the number of joins completed
while (!AllThreadsFinished()) {
  // Wait, keeping in mind that the condition might already have been
  // signalled, in which case it's too late to call pthread_cond_wait,
  // but also keeping in mind that pthread_cond_wait can return spuriously,
  // thus using a while loop
  while (!ThreadFinishStatus.signalled) {
    pthread_cond_wait(&(ThreadFinishStatus.FINISHED), &(ThreadFinishStatus.mutex));
  }
  printf("Main Thread signalled\n");
  ThreadFinishStatus.signalled=FALSE; //Reset signalled
  //check which thread to join
}
pthread_mutex_unlock(&(ThreadFinishStatus.mutex));
1
Ove 1 Июл 2014 в 20:27

Ваш код колоритный.

Предположим, вы запускаете поток, и он завершается до того, как вы захватите мьютекс в main(). Ваш цикл while никогда не запустится, потому что signalled уже был установлен на TRUE выходящим потоком.

Я повторю предложение @ antiduh использовать семафор, который подсчитывает количество мертвых, но не соединенных потоков. Затем вы перебираете количество потоков, созданных в ожидании семафора. Я хотел бы отметить, что POSIX sem_t не похож на pthread_mutex в том, что sem_wait может возвращать EINTR.

0
tmyklebu 1 Июл 2014 в 20:07

Ваш код выглядит нормально. У вас есть один небольшой горн:

 ThreadFinishStatus.signalled==FALSE; //Reset signalled

Это ничего не делает. Он проверяет, является ли сигнал ЛОЖНЫМ, и отбрасывает результат. Это безвредно, потому что вам ничего не нужно делать. (Вы никогда не хотите устанавливать для signalled значение FALSE, потому что это теряет тот факт, что он был сигнализирован. Нет никаких причин отменять его - если поток завершился, то это закончено навсегда.)

Отсутствие входа в цикл while означает, что signalled ИСТИНА. Это означает, что поток уже установил его, и в этом случае нет необходимости входить в цикл, потому что ждать нечего. Так что это нормально.

Также:

ThreadFinishStatus.signalled=TRUE;
if(ThreadFinishStatus.signalled==TRUE)

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

FWIW, я бы предложил изменить архитектуру. Если существующие функции, такие как pthread_join, не делают именно то, что вы хотите, просто не используйте их. Если вы собираетесь иметь структуры, которые отслеживают, какая работа выполняется, полностью отделите это от завершения потока. Поскольку вы уже знаете, какая работа выполняется, что отличает ее от того, когда и как потоки завершаются? Не думайте об этом как о «Мне нужен особый способ узнать, когда поток завершается», а вместо этого думайте об этом: «Мне нужно знать, какая работа выполняется, чтобы я мог делать другие вещи».

0
David Schwartz 3 Июл 2014 в 16:23