【问题标题】:is there any way to wakeup multiple threads at the same time in c/c++有没有办法在c/c++中同时唤醒多个线程
【发布时间】:2021-12-28 07:14:29
【问题描述】:

实际上,我并不是要求线程必须“排队”才能工作,而是我只想通知多个线程。所以我不是在寻找障碍。

有点像condition_variable::notify_all(),但我不希望线程一个一个地唤醒,这可能会导致饥饿(也是多个信号量后期操作中的潜在问题)。有点像:

std::atomic_flag flag{ATOMIC_FLAG_INIT};
void example() {
    if (!flag.test_and_set()) {
//  this is the thread to do the job, and notify others
        do_something();
        notify_others(); // this is what I'm looking for
        flag.clear();
    } else {
//  this is the waiting thread
        wait_till_notification();
        do_some_other_thing();
    }
}

void runner() {
    std::vector<std::threads>;
    for (int i=0; i<10; ++i) {
        threads.emplace_back([]() {
        while(1) {
            example();
        }
    });
    }
// ...
}

那么我如何在 c/c++ 或 posix API 中做到这一点?


对不起,我没有把这个问题说得足够清楚,我会补充一些解释。

我说的不是雷声问题,是的,困扰我的是re-acquire-lock,我试过shared_mutex,还是有问题。

让我将线程分成 2 个部分,1 个作为领导线程,负责写入工作,其他作为工作线程,负责读取工作。

但实际上它们在程序中都是平等的,领导线程是第一个访问作业的线程(您可以将其视为该线程的共享缓冲区下溢)。工作完成后,只需要通知其他工作人员他们可以访问。 如果在这里使用mutex,任何线程都会阻塞其他线程。 举个例子:主线程的jobdo_something()这里是一个read,它阻塞了主线程,从而阻塞了整个系统。

很遗憾,shared_mutex 无法解决这个问题:

void example() {
    if (!flag.test_and_set()) {
    // leader thread:
        lk.lock();
        do_something();
        lk.unlock();
        flag.clear();
    } else {
// worker thread
        lk.shared_lock();
        do_some_other_thing();
        lk.shared_unlock();
    }
}
// outer loop
void looper() {
    std::vector<std::threads>;
    for (int i=0; i<10; ++i) {
        threads.emplace_back([]() {
        while(1) {
            example();
        }
    });
    }
}

在这段代码中,如果领导者的工作已经完成,并且在 unlock 和下一个 lock 之间没有太多可做的事情(记住它们处于循环中),它可能会再次获得锁,离开工作人员的工作不工作,这就是为什么我之前称之为starve

为了解释do_something()中的阻塞,我不希望这部分工作占用我所有的CPU时间,即使领导者的工作还没有准备好(read没有数据到达)

std::call_once 可能仍然不是这个问题的答案。因为如你所见,工人必须等到领导的工作完成。

总而言之,这实际上是一个生产者多消费者的问题。 但我希望消费者能够在产品准备就绪时完成这项工作。 any 可以是生产者或消费者。如果除了第一个发现产品已经用完,线程应该是生产者,因此其他人自动成为消费者。

但不幸的是,我不确定这个想法是否可行

【问题讨论】:

  • 也许我只是对实现细节不够了解,但是但我不希望线程一个接一个地唤醒,这可能会导致饥饿听起来像假设。或者你没有充分解释你的意思。
  • 只是不要假设操作系统可以变魔术。最后的操作系统是一个由凡人编写的程序,因此它无法完成您的要求。
  • 我编辑了问题来解释自己
  • 您是否在寻找std::call_once
  • “唤醒一一”而不是?

标签: c++ multithreading


【解决方案1】:

有点像condition_variable::notify_all(),但我不希望线程一个一个地唤醒,这可能会导致饥饿

原则上,序列化的不是唤醒,而是重新获取锁。

您可以通过使用std::condition_variable_anystd::shared_lock 来避免这种情况——只要没有人获得std::shared_mutex 的排他锁。或者,您可以提供自己的 Lockable 类型。

但是请注意,这不会神奇地让您同时运行比您拥有的内核更多的线程,或者强制调度程序启动它们全部并行运行。它们只会被标记为可运行并正常调度 - 这只会修复您自己代码中的可避免序列化。

【讨论】:

  • thx,这看起来像是解决问题的方法,主线程有额外的锁。我试试看。
【解决方案2】:

听起来你在找call_once

#include <mutex>

void example()
{
  static std::once_flag flag;
  bool i_did_once = false;
  std::call_once(flag, [&i_did_once]() mutable {
    i_did_once = true;
    do_something();
  });
  if(! i_did_once)
    do_some_other_thing();
}

我不明白您的问题与饥饿有何关系。您是否正在考虑雷鸣般的羊群问题?如果do_some_other_thing 具有互斥锁,则可能会出现这种情况,但在这种情况下,您必须更详细地描述您的问题。

【讨论】:

    猜你喜欢
    • 2011-03-28
    • 1970-01-01
    • 2013-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多