【发布时间】:2018-11-08 00:11:08
【问题描述】:
编辑:这不是任何允许在 post() 中锁定互斥锁的问题的重复。请仔细阅读,我需要一个无锁的 post()!如果您没有真正的答案,请勿标记此重复项。
信号量(就像在 linux 中一样)是一个有用的构建块,在 c++ 标准中找不到,在 boost 中也没有(目前)。我主要谈论的是单个进程的线程之间的信号量,通过抢占式调度程序。
我对它们是非阻塞(即无锁)特别感兴趣,除非它确实需要阻塞。也就是说, post() 和 try_wait() 应该始终是无锁的。如果在返回足够多的 post() 之后,它们的调用强烈发生,则 wait() 调用应该是无锁的。 此外,调度程序应该阻止阻塞等待()而不是自旋锁定。 如果我还想要一个带有超时的 wait_for 怎么办 - 它会使实现进一步复杂化,同时仍然避免饥饿?
信号量不在标准中的原因有哪些?
Edit3:所以,我不知道有一个针对标准 P0514R4 的提案可以准确处理这些问题,并且除了专门添加一个 std::semaphore 之外,还有针对此处提出的所有问题的解决方案。 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0514r4.pdf
boost 也没有这些。具体来说,进程间的那些是自旋锁定的。
哪些库支持这样的功能?
是否可以在 windows api 和其他广泛使用的系统上实现它?
编辑:使用 atomics+mutex+condition_variable 来实现无锁是不可能的——你要么在 post 中阻塞,要么在等待中旋转。如果你想要一个无锁的 post(),你不能在 post() 中锁定一个互斥锁。我想在一个可能抢占式调度程序上运行,并且我不希望 post() 被其他使用互斥锁并被抢占的线程阻塞。 所以,这不是重复像C++0x has no semaphores? How to synchronize threads?
这样的问题编辑2: 以下示例实现只是为了演示使用 atomics+mutex+condvar、AFAIK 可以完成的最佳操作。 post() 和 wait() 执行一次无锁 compare_exchange,并且只有在必须时才锁定互斥体。
但是 post() 不是无锁的。更糟糕的是,它可能会被锁定互斥体并被抢占的 wait() 阻塞。
为简单起见,我只实现了 post_one() 和 wait_one_for(Duration),而不是 post(int) 和 wait_for(int,Duration)。此外,我假设标准没有承诺没有虚假唤醒。
class semaphore //provides acquire release memory ordering for the user
{
private:
using mutex_t = std::mutex;
using unique_lock_t = std::unique_lock<mutex_t>;
using condvar_t = std::condition_variable;
using counter_t = int;
std::atomic<counter_t> atomic_count_;
mutex_t mutex_;
condvar_t condvar_;
counter_t posts_notified_pending_;
counter_t posts_unnotified_pending_;
counter_t waiters_running_;
counter_t waiters_aborted_pending_;
public:
void post_one()
{
counter_t start_count = atomic_count_.fetch_add(+1, mo_acq_rel);
if (start_count < 0) {
unique_lock_t lock(mutex_);
if (0 < waiters_running_) {
++posts_notified_pending_;
condvar_.notify_one();
}
else {
if (0 == waiters_aborted_pending_) {
++posts_unnotified_pending_;
}
else {
--waiters_aborted_pending_;
}
}
}
}
template< typename Duration >
bool wait_one_for(Duration timeout)
{
counter_t start_count = atomic_count_.fetch_add(-1, mo_acq_rel);
if (start_count <= 0) {
unique_lock_t a_lock(mutex_);
++waiters_running_;
BOOST_SCOPE_EXIT(&waiters_running_) {
--waiters_running_;
} BOOST_SCOPE_EXIT_END
if( ( 0 == posts_notified_pending_ ) && ( 0 < posts_unnotified_pending_ ) ) {
--posts_unnotified_pending_;
return true;
}
else {
auto wait_result = condvar_.wait_for( a_lock, timeout);
switch (wait_result) {
case std::cv_status::no_timeout: {
--posts_notified_pending_;
return true;
} break;
case std::cv_status::timeout: {
counter_t abort_count = atomic_count_.fetch_add(+1, mo_acq_rel);
if (abort_count >= 0) {
/*too many post() already increased a negative atomic_count_ and will try to notify, let them know we aborted. */
++waiters_aborted_pending_;
}
return false;
} break;
default: assert(false); return false;
}
}
}
return true;
}
bool try_wait_one()
{
counter_t count = atomic_count_.load( mo_acquire );
while (true) {
if (count <= 0) {
return false;
}
else if (atomic_count_.compare_exchange_weak(count, count-1, mo_acq_rel, mo_relaxed )) {
return true;
}
}
}
};
【问题讨论】:
-
您正在寻找POSIX
sem_trywait(3p)的纯 ISO C++11 版本? (也是Linuxsem_trywait(3)man page)。您是否验证过 Linux 的 trywait 是真正无锁的,而在失败情况下不进行任何系统调用? (例如,设置一个已经获取的信号量和睡眠,并在另一个线程中循环 trywait。strace -f以跟踪系统调用。) -
@PeterCordes,我的问题不是 linux 特有的,尽管我知道 linux 的信号量。那么 sem_post 和 sem_trywait 在内部使用 futex 机制。我不知道实现的细节,如果它是无锁的,或者它只在内核模式下进行自旋锁,那么这就是我想要的。如果 futex 在用户模式下导致自旋锁,那么我想我可以用 mutex+condition_varaiable 做同样的事情。我的部分问题是得到关于它在 linux 中如何工作的答案,然后是 windows 和任何广泛使用的系统。
-
应该说得更清楚一点:sem_post/wait/trywait 在用户模式下进行一次 compare_exchange,并且只有在他们必须在内部使用 futex 机制的情况下。我不知道实现的细节,如果它是无锁的,或者它只在内核模式下进行自旋锁,那么这就是我想要的。如果 futex 在用户模式下导致自旋锁,那就不太好(有点) - 但是我什至不能用 mutex+condvar 做到这一点。
-
有一个不错的(主要是)跨平台的高性能实现,由 Jeff Preshing 编写,我盗用它与我的无锁队列的阻塞版本一起使用(随后略有增强)。你可以在这个文件的顶部找到它:github.com/cameron314/concurrentqueue/blob/master/…你特别感兴趣的类叫做
LightweightSemaphore,它只在操作系统级别阻塞,如果它必须(一个原子计数器维护在用户空间中)快速路径)。 -
@itaj 我还没有将它与 Linux 上的其他做事方式进行基准测试。我也没有最近的内核,所以这样做可能不会很有趣。当一次增加/减少许多时,用户模式计数器仍然很有用,我认为底层接口不支持。显然,您的用例可能不需要这个相当特殊的功能:-)
标签: c++ c++11 lock-free thread-synchronization