std :: atomic :: notify_one可以解开多个线程

发布于 2025-02-06 07:12:00 字数 475 浏览 2 评论 0 原文

根据cppreference, std :: atomic< t> t> ) 将通知 至少 一个正在等待所述原子的线程。这意味着,根据标准,它可以解开多个线程。这与 ,它指定它将解开(不超过)一个线程。

这种差异从何而来?这不使用相同的基本机制吗?就标准库的实现而言,所有流行的库是否都有机会实际上可以对此呼叫进行覆盖,或者是否有一些完全没有阻止一个?

According to cppreference, std::atomic<T>::notify_one() will notify at least one thread that is waiting on said atomic. This means that according to the standard it could unblock more than one thread. This is in contrast to std::condition_variable::notify_one(), which specifies that it will unblock (no more than) one thread.

Where does this difference come from? Does this not use the same underlying mechanism? As far as implementations of the standard library go, do all of the prevalent ones have a chance of actually unblocking multiple with this call, or are there some that always unblock exactly one?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦里寻她 2025-02-13 07:12:00

两者都 std std :: 允许随时伪造,包括在 notify_one 的特定时间。

因此,就规范而言,尽管确实确实看来该标准对两个使用不同的措辞(“ 至少一个 [atomics.types.operation]/32 ,但只是 [thread.condition.condition.condition.condvar]/5 ),我没有认为这里有任何规范上的差异。当调用 notify_one 时,只要它至少是一个(如果有等待的话),则可以自由地解开两个等待操作的任何数量的线程。对于 std :: atomic :: notify_one 以及 std :: preengation_variable :: notify_one_one (即“不超过)) “是错误的)。


在较早的修订中,该提案对于 std :: Atomic 等待操作仍然说“ 一个”,而不是“ 至少一个”。

修订版 >,根据哪些措辞改进,部分是根据LWG电视会议的反馈。我认为这些电视会议的任何文档都没有公开可用,因此我无法判断这种措辞更改是否有特定意图。

也许应该避免误解,因为在不同的段落中提到了虚假的解密,或者可能是为了表达有一些实现的实现,这些实施可以解除多个/所有等待操作,这些操作是专门为支持的。另请参阅问题和此答案下的评论。

Both std::atomic::wait and std::condition_variable::wait are allowed to unblock spuriously at any time, including at the specific time that notify_one is called.

So in terms of the specification, although it does indeed seem that the standard uses different wording for the two ("at least one" in [atomics.types.operation]/32 but just "one" in [thread.condition.condvar]/5), I don't think there is any normative difference here. The implementation is free to unblock any number of threads for both waiting operations when notify_one is called, as long as it is at least one (if any are waiting). This is true for std::atomic::notify_one as well as std::condition_variable::notify_one (i.e. "(no more than)" is wrong).


In an earlier revision of the proposal [P1135R4] for the std::atomic waiting operations it still said "one" instead of "at least one" in the proposed wording.

That was changed with revision [P1135R5], according to which wording improvements were made partly in response to feedback from LWG teleconferences. I don't think any documentation of these teleconferences is publicly available, so I can't tell whether there was a specific intent in this wording change.

Maybe it is supposed to avoid misunderstandings since the spurious unblocking is mentioned in a different paragraph or maybe it is meant to convey that there are implementations which do unblock multiple/all waiting operations that are specifically intended to be supported as such. See also comments under the question and this answer.

故事↓在人 2025-02-13 07:12:00

据我所知,他们可以使用相同的机制,但不必这样做。我通常使用的实现是libstdc ++,其原子:: notify_one()绝对不同。

条件_variable相当不透明,在bits/std_mutex.h标题中以__condvar的形式实现。它的只需调用pthread包装器:

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

而notify_all()而不是调用__gthread_cond_broadcast。

这与原子学形成鲜明对比,这些原子的参与得多,但最终使用一系列函数来通知线程,从 __ atomic_notify_address

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

__ waiter_pool_base :: _ m_notify :: _ m_notify

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
}

只需使用条件变量通知一个或所有线程即可。否则,它是使用futex实现的,并且也可能唤醒(最多? v3/inclage/bits/atomic_wait.h#l119“ rel =“ noreferrer”> __ platform_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);
}

As far as I can tell, they can use the same mechanism, but don't have to. The implementation I typically use is libstdc++ and its atomic::notify_one() is definitely different.

The condition_variable is fairly opaque, implemented as __condvar in the bits/std_mutex.h header. Its notify_one() just calls a pthread wrapper:

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

whereas notify_all() instead calls __gthread_cond_broadcast.

This contrasts with the atomics, which are much more involved, but end up using a sequence of functions for notifying threads, starting with __atomic_notify_address:

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

__waiter_pool_base::_M_notify:

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
}

So if there is no _GLIBCXX_HAVE_PLATFORM_WAIT, libstdc++ will simply use a condition variable to notify either one or all threads. Otherwise, it's implemented using a futex, and may also wake (up to?) INT_MAX threads, see __platform_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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文