【问题标题】:The version of pthread_join() that does not block main(): POSIX不阻塞 main() 的 pthread_join() 版本:POSIX
【发布时间】:2014-07-01 19:02:38
【问题描述】:

我正在尝试编写一个在调用pthread_join() 时不会阻塞main() 的代码: 即基本上试图实现下面提到的我之前的问题:

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

以及对应的解释在:

pthreads - Join on group of threads, wait for one to exit

根据建议的答案:

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) 我的main()中对应的部分pthread_cond_wait()怎么写?

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 中的返回值 即在我的 main 中放置以下语句的位置:

`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;
    }

【问题讨论】:

  • 取决于平台,您可以使用(非便携式)pthread_tryjoin_np 功能。
  • 创建分离线程是否需要过多的重新编码?
  • 按预期工作,打印“blah blah Main Thread signalled”并退出。你观察到什么?
  • 该代码中存在竞争条件,具体取决于线程完成的速度。使用计数信号量,线程在完成时递增,主递减以找出线程何时完成。使用普通互斥锁保护对共享数据结构的访问。获取互斥锁只是为了立即释放它是愚蠢的。

标签: c multithreading posix


【解决方案1】:

这是一个程序示例,该程序使用计数信号量来观察线程是否完成,找出它是哪个线程,并查看来自该线程的一些结果数据。这个程序对锁很有效 - 服务员不会被虚假唤醒(注意线程如何在释放互斥锁保护共享状态之后才发布到信号量)。

这种设计允许主程序在某个线程完成后立即处理来自某个线程的计算结果,并且不需要主程序等待所有线程完成。如果每个线程的运行时间变化很大,这将特别有用。

最重要的是,这个程序不会死锁也不会竞争。

#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;
}

【讨论】:

    【解决方案2】:

    Pthread 条件没有“内存”;如果在 pthread_cond_wait 之前调用 pthread_cond_signal,则 pthread_cond_wait 不会返回,这就是为什么在调用 pthread_cond_wait 之前检查谓词很重要,如果它是真的就不要调用它。但这意味着操作,在这种情况下,“检查要加入的线程”应该只取决于谓词,而不是取决于是否调用了 pthread_cond_wait。

    此外,您可能希望让 while 循环真正等待所有线程终止,而您现在没有这样做。

    (另外,我认为关于“signalled==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));
    

    【讨论】:

    • 我不同意你的分析。 signalled==FALSE 是无害的,因为它没有效果。你说得很对,信号可能不是假的,但这很好。这行代码什么都不做,所以在任何情况下都不会弄乱信号。
    • @DavidSchwartz:我所指的答案说它是无害的,因为信号总是错误的,这是错误的(尽管我认为它现在已被编辑)。其次,将应该做某事的代码行变成的代码行的错误绝不是我认为对代码功能无害的事情.
    • 我确实同意,但是,以这种方式使用信号(显式重置)不是好的风格,因为它可能很难正确处理(在这种情况下,可能有多个线程在等待每次重置时加入,如果您未能处理该信号,则不会重新发出信号)。如果内部循环直接检查 ThreadFinishStatus 数组(或其他等价物)并完全摆脱“signalled”,可能会更安全。
    【解决方案3】:

    你的代码很活泼。

    假设您启动一个线程并在您获取main() 中的互斥锁之前完成。您的while 循环将永远不会运行,因为退出线程已经将signalled 设置为TRUE

    我会回应@antiduh 的建议,即使用一个信号量来计算死但未加入的线程的数量。然后,您循环到在信号量上等待产生的线程数。我要指出,POSIX sem_t 不像 pthread_mutex,因为 sem_wait 可以返回 EINTR。

    【讨论】:

    • 我完全不同意你的分析。没错,while 循环不会运行,但为什么要在这种情况下运行呢? while 循环的重点是在线程完成时退出,这正是它在这种情况下所做的。
    • @DavidSchwartz:它启动了多个线程......(编辑:我们两个似乎不同意代码应该做什么。你回答“等待一个”并且我给出了“等待所有人”的部分答案。我不清楚哪个 OP 想要,因为他链接到“等待一个”帖子,但他的代码看起来像是“等待所有人”的尝试。)跨度>
    • 如果他只是想等待所有线程,阻塞直到它们都停止,他可以使用pthread_join
    • @DavidSchwartz:是的,除非他想在它们完成后立即清理它们。
    【解决方案4】:

    您的代码看起来不错。你有一个小号:

     ThreadFinishStatus.signalled==FALSE; //Reset signalled
    

    这无济于事。它测试 signaled 是否为 FALSE 并丢弃结果。这是无害的,因为你不需要做任何事情。 (您从不想将signalled 设置为FALSE,因为这会丢失它已发出信号的事实。永远没有任何理由取消它-如果线程完成,那么它就永远完成了。 )

    不进入while 循环意味着signalled 为TRUE。这意味着线程已经设置了它,在这种情况下不需要进入循环,因为没有什么可以等待的。所以没关系。

    还有:

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

    没有必要测试你刚刚设置的东西。不是说套装会失败。

    FWIW,我建议重新架构。如果像pthread_join 这样的现有功能不能完全满足您的要求,请不要使用它们。如果您要拥有跟踪已完成工作的结构,则将其与线程终止完全分开。既然您已经知道完成了哪些工作,那么线程终止的时间和方式有什么不同?不要认为这是“我需要一种特殊的方法来知道线程何时终止”,而应该认为这是“我需要知道完成了哪些工作,以便我可以做其他事情”。

    【讨论】:

    • 其实我的要求是只要一个线程完成(在5个线程中),我想根据刚刚完成的线程的返回值在main()中执行一些任务。如果我像这样使用 join - for(i=0;i&lt;THREAD_NUM;i++){pthread_join(T[i],&amp;(arg[i].returnStatus)); printf("Main resumed after Thread Finished :%d\n",arg[i].returnStatus);} 假设 thread 2 先完成;我的main() 将被阻止直到threads 0 and 1 完成,我无法立即处理thread 2 返回的结果;这就是我想要实现的目标
    • @GauravK 这就是我的想法。那么我的cmets是正确的。您可以通过获取互斥锁并检查signalled 变量来检查每个线程是否已完成。
    • @@David 我在处理这部分代码时遇到了问题-while((ThreadFinishStatus.signalled != TRUE){pthread_cond_wait(&amp;(ThreadFinishStatus.FINISHED), &amp;(ThreadFinishStatus.mutex));printf("Main Thread signalled\n");ThreadFinishStatus.signalled==FALSE; //Reset signalled//check which thread to join /*Put the join statement here*/}。我的意思是我无法在任务完成后立即实现,只检查那个特定线程的返回值; To obtain the return value I must use pthread_join()
    • @GauravK 如果它困扰你,让线程将其返回值存储在结构中。如果pthread_join 不能完全按照您想要的方式工作,请不要使用它。它不会做任何您自己做不到的事情(除了收获线程,您可以根据需要将其分离)。
    猜你喜欢
    • 2010-09-09
    • 2015-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多