简体   繁体   English

在没有互斥锁的情况下立即唤醒所有线程

[英]Waking up all of the threads at once, without a mutex

My program processes database rows in different threads depending on one of the row's fields. 我的程序根据行的字段之一处理不同线程中的数据库行。 The main thread spawns off the "workers", performs a query, and then, for each row, needs to wake up all of the workers for one of them to consume the row. 主线程产生“工人”,执行查询,然后,对于每一行,需要唤醒所有工作人员以使其中一人消耗该行。

Now, using pthread_cond_broadcast() seems like the most logical choice. 现在,使用pthread_cond_broadcast()似乎是最合乎逻辑的选择。 However, the workers in this case must all wait inside pthread_cond_wait() using the same mutex . 但是,在这种情况下,工作者必须使用相同的互斥锁在pthread_cond_wait()内等待。

In my case this is suboptimal, because it means, the workers will be woken up one at a time (which I do not need) -- instead of all at once . 在我的情况下,这是次优的,因为这意味着,工人将被唤醒一次 (我不需要) - 而不是一次全部唤醒。 Yes, I do want them all to wake up -- they would all read one field from the new DB-row, after which all but one will go back to waiting for the next row. 是的,我确实希望他们醒来 - 他们都会从新的DB行中读取一个字段,之后除了一个之外的所有字段都将返回等待下一行。 I do not need to synchronize them. 我不需要同步它们。

I thought, I'd use a dummy thread-specific mutex with the pthread_cond_wait() in each thread, but that is not working (only one thread is woken up). 我想,我会在每个线程中使用带有pthread_cond_wait()的虚拟线程专用互斥锁,但这不起作用(只唤醒一个线程)。 The standard says, waiting for the same condition variable using different mutexes (as I do) is undefined. 标准说,使用不同的互斥锁等待相同的条件变量(就像我一样)是未定义的。

So, is there a way to notify all threads at once ? 那么,有没有办法立即通知所有线程? Thanks! 谢谢!

I think you need to describe more about the problem and why you're (attempting) to do it this way to begin with. 我认为你需要更多地描述这个问题,以及你为什么(试图)这样开始这样做。 I wouldn't be surprised if the best way is to do something completely different that doesn't involve waking up all threads at once without a mutex. 如果最好的方法是做一些完全不同的事情而不涉及在没有互斥锁的情况下立即唤醒所有线程,我不会感到惊讶。

To me, your description sounds like: 对我来说,你的描述听起来像:

  • Main thread spawns several threads (where spawning a thread is relatively expensive) 主线程产生多个线程(产生线程相对昂贵)
  • Main thread does a query, while the spawned threads start, do very little, then block (where starting/restarting and blocking are relatively expensive) 主线程执行查询,而生成的线程启动,执行很少,然后阻塞(启动/重启和阻塞相对昂贵)
  • For each row the main thread wakes up every thread (relatively expensive restarting and blocking) and every thread except one of them goes back to waiting (very wasteful) 对于每一行,主线程唤醒每个线程(相对昂贵的重启和阻塞),除了其中一个线程之外的每个线程都会回到等待(非常浪费)

Without knowing why you're doing any of this, I'd assume that not using any threads at all would be faster (eg that the main thread would be able to process a row faster than the main thread can examine the row and tell one spawned thread to process it and hassle other threads for no reason). 在不知道你为什么要这样做的情况下,我假设根本不使用任何线程会更快(例如,主线程能够比主线程检查行更快地处理行并告诉一行产生线程来处理它并无缘无故地麻烦其他线程)。

If processing a row takes a long time, then I'd consider having worker threads waiting on a FIFO queue, such that the main thread pushes a "process this row" command onto the queue and the first thread that grabs it from the queue processes that row. 如果处理一行需要很长时间,那么我会考虑让工作线程在FIFO队列上等待,这样主线程就会将“处理此行”命令推送到队列中,并将第一个线程从队列进程中抓取它那一排。

Of course I have no idea why you want to do what you want to do, and therefore any suggestion is just a guess. 当然我不知道你为什么要做你想做的事情,所以任何建议都只是猜测。

TL;DR: I think your question is a bit like someone that wants to lose weight asking "what is the best way to chop off my own legs" (where the most practical answer has nothing to do with the question actually asked). TL; DR:我认为你的问题有点像想要减肥的人,问“什么是切断自己腿的最佳方法”(最实际的答案与实际问题无关)。

With condition variables, the assumption is that there is some associated "condition" (the data row in your case) that needs to be checked and updated exclusively (hence the mutex). 对于条件变量,假设存在一些相关的“条件”(在您的情况下为数据行),需要进行独占检查和更新(因此是互斥锁)。 Whichever other mechanism you choose, you will need to figure out how to ensure exclusive access to your "work queue" (whether it's a single slot or a real queue). 无论您选择哪种其他机制,您都需要弄清楚如何确保对“工作队列”的独占访问(无论是单个插槽还是真实队列)。

With a shared queue, you will always have 2 writers (main thread + intended worker) and N-1 readers to the data structure. 使用共享队列,您将始终拥有2个写入器(主线程+预期工作者)和N-1个读取器到数据结构。 You could use read-write locks (rwlock) to ensure integrity. 您可以使用读写锁(rwlock)来确保完整性。

Alternatively you could have N separate queues (one per worker). 或者,您可以拥有N个单独的队列(每个工作一个队列)。 You would push a copy of the data row to each worker. 您可以将数据行的副本推送给每个工作人员。

As far as waking up several threads at once: you could have your workers "sleep" (eg with select()) and wake them up with pthread_signal() (in a loop). 至于一次唤醒多个线程:你可以让你的工作人员“睡觉”(例如用select())并用pthread_signal()(在循环中)唤醒它们。

You could also use pthread_barrier_wait() . 您也可以使用pthread_barrier_wait()

The pthread_barrier_wait() function shall synchronize participating threads at the barrier referenced by barrier. pthread_barrier_wait()函数应在ba​​rrier引用的屏障处同步参与的线程。 The calling thread shall block until the required number of threads have called pthread_barrier_wait() specifying the barrier. 调用线程将阻塞,直到所需数量的线程调用指定屏障的pthread_barrier_wait()。

When the required number of threads have called pthread_barrier_wait() specifying the barrier, the constant PTHREAD_BARRIER_SERIAL_THREAD shall be returned to one unspecified thread and zero shall be returned to each of the remaining threads. 当所需数量的线程调用指定屏障的pthread_barrier_wait()时,常量PTHREAD_BARRIER_SERIAL_THREAD将返回到一个未指定的线程,并且零将返回到每个剩余线程。 At this point, the barrier shall be reset to the state it had as a result of the most recent pthread_barrier_init() function that referenced it. 此时,屏障应重置为最近引用它的pthread_barrier_init()函数所具有的状态。

  1. initialize the barrier with pthread_barrier_init() (with count = 1 + # of workers) 使用pthread_barrier_init()初始化障碍(计数= 1 +工人#)
  2. in each worker, call pthread_barrier_wait() in a loop; 在每个worker中,在循环中调用pthread_barrier_wait(); when that returns, new data is ready 当返回时,新数据准备就绪
  3. in the main thread, call pthread_barrier_wait() to signal the workers 在主线程中,调用pthread_barrier_wait()来表示工作者

Unfortunately (as the OP notes), in the next iteration, no worker will be woken up until the previously activated worker finishes its job. 不幸的是(正如OP注释),在下一次迭代中,在先前激活的工人完成其工作之前,不会唤醒任何工人。

A simpler architecture would have the main thread dispatch events to the proper worker (instead of waking all workers up and having them figure out which one is the intended recipient). 一个更简单的体系结构将主线程调度事件发送给适当的工作者(而不是唤醒所有工作者并让他们弄清楚哪一个是预期的接收者)。 Unless you have as many cores as workers, the tests will not really happen in parallel anyway. 除非你拥有与工作人员一样多的内核,否则测试并不会真正并行发生。 Also, even if you have enough cores to let the workers run in parallel, N-1 of them will not learn that the "winner" has taken up the job before they finish testing, so the total amount of work across all cores is higher. 此外,即使您有足够的内核让工作人员并行运行,N-1也不会知道“胜利者”在完成测试之前已经完成了工作,因此所有核心的工作总量更高。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM