pthread_join - 等待多个线程

时间:2009-01-10 15:06:30

标签: c++ multithreading posix

使用POSIX线程& C ++,我有一个“插入操作”,只能一次安全地完成。

如果我有多个线程等待使用pthread_join插入然后生成一个新线程 什么时候结束它们是否会立即收到“线程完整”信号并产生多个插入,或者可以安全地假设接收“线程完成”信号的线程首先会产生一个阻止其他线程创建新线程的新线程。

/* --- GLOBAL --- */
pthread_t insertThread;



/* --- DIFFERENT THREADS --- */
// Wait for Current insert to finish
pthread_join(insertThread, NULL); 

// Done start a new one
pthread_create(&insertThread, NULL, Insert, Data);

感谢您的回复

该程序基本上是一个巨大的哈希表,它通过套接字从客户端接收请求。

每个新的客户端连接都会产生一个新线程,然后它可以执行多个操作,特别是查找或插入。查找可以并行进行。但插入需要“重新组合”到一个线程中。您可以说查找操作可以在不为客户端生成新线程的情况下完成,但是它们可能需要一段时间才会导致服务器锁定,丢弃新请求。该设计尝试尽可能地减少系统调用和线程创建。

但是现在我知道这种方式并不安全,我认为我应该能够一起拼凑一些东西

由于

9 个答案:

答案 0 :(得分:3)

来自opengroup.org on pthread_join

  

指定同一目标线程的多个同时调用pthread_join()的结果未定义。

所以,你真的不应该有几个线程加入你以前的insertThread。

首先,当您使用C ++时,我建议使用boost.thread。它们类似于线程的POSIX模型,也适用于Windows。它可以帮助您使用C ++,即通过使函数对象更容易使用。

其次,为什么要在开始下一个元素之前等待前一个元素完成时,为什么要启动一个新的线程来插入一个元素?似乎不是多线程的经典使用。

虽然......一个经典的解决方案是让一个工作线程从事件队列中获取作业,而其他线程将操作发布到事件队列中。

如果你真的只想以现在的方式保持它,你必须这样做:

  • 创建条件变量,例如insert_finished
  • 所有想要插入的线程,等待条件变量。
  • 只要一个线程完成插入,它就会触发条件变量。
  • 由于条件变量需要互斥锁,您只需通知所有等待线程,它们都想要开始插入,但由于一次只有一个线程可以获取互斥锁,所有线程都会执行顺序插入。

但是你应该注意你的同步没有以过于特殊的方式实现。由于这被称为insert,我怀疑你想要操纵数据结构,所以你可能想要首先实现一个线程安全的数据结构,而不是共享数据结构访问和所有客户端之间的同步。我还怀疑只有insert会有更多的操作,这需要正确的同步......

答案 1 :(得分:2)

根据Single Unix规范:“指定同一目标线程的多个同时调用pthread_join()的结果是未定义的。”

实现单个线程来获取任务的“正常方式”是设置条件变量(不要忘记相关的互斥锁):空闲线程在pthread_cond_wait()(或pthread_cond_timedwait())中等待,以及当完成工作的线程完成后,它会使用pthread_cond_signal()唤醒其中一个空闲的线程。

答案 2 :(得分:1)

是的,因为大多数人建议最好的方法似乎是从队列中读取工作线程。

下面的一些代码片段
    pthread_t       insertThread = NULL;
    pthread_mutex_t insertConditionNewMutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_t insertConditionDoneMutex    = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t  insertConditionNew      = PTHREAD_COND_INITIALIZER;
    pthread_cond_t  insertConditionDone     = PTHREAD_COND_INITIALIZER;

       //Thread for new incoming connection
        void * newBatchInsert()
        {
           for(each Word)
           {
                            //Push It into the queue
                            pthread_mutex_lock(&lexicon[newPendingWord->length - 1]->insertQueueMutex);
                                lexicon[newPendingWord->length - 1]->insertQueue.push(newPendingWord);
                            pthread_mutex_unlock(&lexicon[newPendingWord->length - 1]->insertQueueMutex);

           }

                    //Send signal to worker Thread
                    pthread_mutex_lock(&insertConditionNewMutex);
                        pthread_cond_signal(&insertConditionNew);
                    pthread_mutex_unlock(&insertConditionNewMutex);

                    //Wait Until it's finished
                    pthread_cond_wait(&insertConditionDone, &insertConditionDoneMutex);

        }


            //Worker thread
            void * insertWorker(void *)
            {

                while(1)        
                {

                    pthread_cond_wait(&insertConditionNew, &insertConditionNewMutex);

                    for (int ii = 0; ii < maxWordLength; ++ii)
                    {                   
                            while (!lexicon[ii]->insertQueue.empty())
                            {

                                queueNode * newPendingWord = lexicon[ii]->insertQueue.front();


                                lexicon[ii]->insert(newPendingWord->word);

                                pthread_mutex_lock(&lexicon[ii]->insertQueueMutex);
                                lexicon[ii]->insertQueue.pop();
                                pthread_mutex_unlock(&lexicon[ii]->insertQueueMutex);

                            }

                    }

                    //Send signal that it's done
                    pthread_mutex_lock(&insertConditionDoneMutex);
                        pthread_cond_broadcast(&insertConditionDone);
                    pthread_mutex_unlock(&insertConditionDoneMutex);

                }

            }

            int main(int argc, char * const argv[]) 
            {

                pthread_create(&insertThread, NULL, &insertWorker, NULL);


                lexiconServer = new server(serverPort, (void *) newBatchInsert);

                return 0;
            }

答案 3 :(得分:0)

其他人已经指出这有不确定的行为。我只是补充说,完成任务的最简单方法(只允许一个线程执行部分代码)是使用一个简单的互斥锁 - 你需要执行该代码的线程是MUTally EXclusive,这就是mutex来到的地方它的名字: - )

如果需要在特定线程(如Java AWT)中运行代码,则需要条件变量。但是,您应该三思而后行,这个解决方案是否真的有回报。想象一下,如果每秒调用“插入操作”10000次,则需要多少个上下文切换。

答案 4 :(得分:0)

正如您刚才提到的那样,您使用的哈希表有几个与插入并行的查找,我建议您检查是否可以使用并发哈希表。

当您同时插入元素时,确切的查找结果是不确定的,这样的并发哈希映射可能正是您所需要的。我没有在C ++中使用并发哈希表,但是因为它们在Java中可用,所以你肯定会找到一个用C ++编写的库。

答案 5 :(得分:0)

我发现唯一支持插入而不锁定新查找的库 - Sunrise DD(我不确定它是否支持并发插入)

然而,从谷歌Sparse Hash map切换的内存使用量增加了一倍以上。查找应该很少发生,而不是尝试和编写我自己的库 结合了两者的优点,我宁愿锁定表暂停查找,同时安全地进行更改。

再次感谢

答案 6 :(得分:0)

在我看来,您希望将插入序列化为哈希表。

为此你想要一个锁 - 不要产生新的线程。

答案 7 :(得分:0)

从您的描述看起来非常低效,因为每次要插入内容时都要重新创建插入线程。创建线程的成本不是0.

这个问题的一个更常见的解决方案是生成一个等待队列的插入线程(即在循环为空时坐在循环中)。然后,其他线程将工作项添加到队列中。插入线程按照添加顺序(或根据需要按优先级)选择队列中的项目并执行相应的操作。

您所要做的就是确保对队列的添加受到保护,以便一次只有一个线程可以访问修改实际队列,并且插入线程不会进行繁忙等待,而是在没有任何东西时休眠在队列中(参见条件变量)。

答案 8 :(得分:0)

理想情况下,您不希望在单个进程中使用多个线程池,即使它们执行不同的操作也是如此。线程的可恢复性是一个重要的体系结构定义,如果使用C,则会导致在主线程中创建pthread_join。

当然,对于C ++线程池又称ThreadFactory,我们的想法是保持线程原语是抽象的,这样就可以处理传递给它的任何函数/操作类型。

一个典型的例子是一个Web服务器,它将具有连接池和线程池,它们为连接提供服务,然后进一步处理它们,但是,所有这些都是从一个共同的线程池进程派生的。

摘要:在主线程以外的任何地方避免PTHREAD_JOIN。