当前位置: 首页 > 知识库问答 >
问题:

std::atomic::notify\u一个可以取消阻止多个线程

长孙嘉
2023-03-14

根据cppreference,std::atomic::notify\u one()将通知至少一个等待所述atomic的线程。这意味着根据标准,它可以解锁多个线程。这与std::condition\u variable::notify\u one()相反,后者指定它将解锁(不超过)一个线程。

这种差异来自哪里?这不是使用相同的底层机制吗?就标准库的实现而言,是否所有流行的都有机会通过此调用实际解除屏蔽多个,还是有一些总是完全解除屏蔽一个?

共有2个答案

刘元青
2023-03-14

据我所知,他们可以使用相同的机制,但不必如此。我通常使用的实现是libstdc,它的atomic::notify_one()绝对不同。

condition\u变量相当不透明,在bits/std\u互斥体中实现为\uu condvar。h收割台。其notify\u one()只调用pthread包装器:

void
notify_one() noexcept
{
  int __e __attribute__((__unused__)) = __gthread_cond_signal(&_M_cond);
  __glibcxx_assert(__e == 0);
}

而notify_all()则调用__gthread_cond_broadcast。

这与原子相比,原子涉及的内容更多,但最终使用一系列函数通知线程,从\uu原子\u notify\u地址开始:

template<typename _Tp>
void
__atomic_notify_address(const _Tp* __addr, bool __all) noexcept
{
  __detail::__bare_wait __w(__addr);
  __w._M_notify(__all);
}

__waiter_base::_M_notify:

void
_M_notify(bool __all, bool __bare = false)
{
  if (_M_laundered())
  {
    __atomic_fetch_add(_M_addr, 1, __ATOMIC_SEQ_CST);
    __all = true;
  }
  _M_w._M_notify(_M_addr, __all, __bare);
}

__服务员\u池\u基::\u M\u通知:

void
_M_notify(const __platform_wait_t* __addr, bool __all, bool __bare) noexcept
{
  if (!(__bare || _M_waiting()))
    return;

#ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
  __platform_notify(__addr, __all);
#else
  if (__all)
    _M_cv.notify_all();
  else
    _M_cv.notify_one();
#endif
}

因此,如果没有\u GLIBCXX\u HAVE\u PLATFORM\u WAIT,libstdc将只使用条件变量通知一个或所有线程。否则,它将使用futex实现,并且可能会唤醒(多达?)INT\u MAX threads,请参阅\uu platform\u notify:

template<typename _Tp>
void
__platform_notify(const _Tp* __addr, bool __all) noexcept
{
  syscall (SYS_futex, static_cast<const void*>(__addr),
           static_cast<int>(__futex_wait_flags::__wake_private),
           __all ? INT_MAX : 1);
}
狄珂
2023-03-14

std::atomic::etcstd::condition_variable::都可以在任何时候解除屏蔽,包括在调用notify_one的特定时间。

所以在规范方面,虽然看起来确实标准对两者使用了不同的措辞([atomics.types.operation]/32中的“至少一个”而在[thread.condition.condvar]/5中只是“一个”),但我不认为这里有任何规范性差异。实现可以在调用notify_one时自由解除屏蔽两个等待操作的任意数量的线程,只要它至少是一个(如果有的话正在等待)。对于std::atomic::notify_one以及std::condition_variable::notify_one(即“(不超过)”是错误的)都是如此。

st tomic等待操作的提案[P1135R4]的早期修订中,它在提议的措辞中仍然说“一个”而不是“至少一个”。

这一点随着修订[P1135R5]而改变,根据修订[P1135R5],措辞改进部分是为了响应LWG电话会议的反馈。我认为这些电话会议没有任何留档是公开的,所以我不知道这种措辞变化是否有具体意图。

也许它是为了避免误解,因为虚假的解除屏蔽在不同的段落中被提及,或者它可能是为了传达有一些实现可以解除屏蔽多个/所有等待操作,这些操作专门打算被支持。另请参阅问题和本答案下的评论。

 类似资料:
  • angular-atomic-notify 是显示消息的 Angular 模块。 demo: Online demo

  • 我有一个用于读写器的环形缓冲区。我跟踪环形缓冲区中的数字if条目,不允许覆盖尚未读取的条目。我使用std::condition\u variable wait()和notify\u one()来同步读写器。读卡器的基本条件是 这一切似乎都奏效了,但有一件事我不明白。当读写器调用notify\u one()时,不会导致上下文切换。我读过并理解它是这样工作的。然而,在写入程序写入一个条目以填充缓冲区的

  • 我读过C 11内存模型中的并发性:原子性和易失性以及std::memory\u order\u seq\u cst的工作原理,它没有多大帮助,直接回答了我的问题。 来自https://en.cppreference.com/w/cpp/thread/condition_variable/notify_one: notify one()和wait for()的三个原子部分(解锁等待、唤醒和锁定)的效

  • 在Flink中,像“平面地图”、“地图”等运算符称为任务,如果我将平面地图的并行度设置为30,那么这个任务有30个子任务。 现在,如果我只有一个插槽,它会在一个插槽中产生多个线程吗?还是每个插槽只有一个线程? Flink会在该插槽中简单地创建30个线程,还是使用类似线程池的东西? 以上不是一个恰当的例子。 假设在作业中我有操作符flatMap和map,它们都有并行度1,我只有一个插槽,这个插槽会创

  • 我试着编译简单的代码 对于Clang++3.2(从llvm.org下载为LLVM3.2;在mac OS.x 10.8.3上,此操作失败,出现错误 当我使用/usr/bin/clang++时(这是OS或Xcode附带的),它编译得很好。在这两种情况下,libc++都是/usr/lib/c++/v1。 我错过了什么?LLVM3.2附带了另一个libc++吗?(我在Clang3.2树中找不到任何东西)。

  • 考虑以下示例代码,其中线程A在队列上推送函数,线程B在从队列中弹出时执行这些函数: 其中是一个并发队列,它有一个和一个函数,每个返回一个指示给定操作是否成功。因此,如果已满,则返回,如果为空,则返回。 现在我想知道代码是否在所有情况下都是线程安全的。让我们假设线程B的失败并即将调用