C++0x 没有信号量?如何同步线程?
C++0x 真的不会有信号量吗? Stack Overflow 上已经有一些关于信号量使用的问题。我一直使用它们(posix 信号量)来让一个线程等待另一个线程中的某个事件:
void thread0(...)
{
doSomething0();
event1.wait();
...
}
void thread1(...)
{
doSomething1();
event1.post();
...
}
如果我用互斥锁来做到这一点:
void thread0(...)
{
doSomething0();
event1.lock(); event1.unlock();
...
}
void thread1(...)
{
event1.lock();
doSomethingth1();
event1.unlock();
...
}
问题:它很难看,并且不能保证 thread1 首先锁定互斥锁(鉴于相同)线程应该锁定和解锁互斥体,您也不能在 thread0 和 thread1 启动之前锁定 event1)。
那么既然 boost 也没有信号量,那么实现上述目标的最简单方法是什么?
Is it true that C++0x will come without semaphores? There are already some questions on Stack Overflow regarding the use of semaphores. I use them (posix semaphores) all the time to let a thread wait for some event in another thread:
void thread0(...)
{
doSomething0();
event1.wait();
...
}
void thread1(...)
{
doSomething1();
event1.post();
...
}
If I would do that with a mutex:
void thread0(...)
{
doSomething0();
event1.lock(); event1.unlock();
...
}
void thread1(...)
{
event1.lock();
doSomethingth1();
event1.unlock();
...
}
Problem: It's ugly and it's not guaranteed that thread1 locks the mutex first (Given that the same thread should lock and unlock a mutex, you also can't lock event1 before thread0 and thread1 started).
So since boost doesn't have semaphores either, what is the simplest way to achieve the above?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
您可以轻松地从互斥体和条件变量构建一个互斥体:
You can easily build one from a mutex and a condition variable:
基于 Maxim Yegorushkin 的回答,我尝试以 C++11 风格制作示例。
Based on Maxim Yegorushkin's answer, I tried to make the example in C++11 style.
我决定尽可能地以标准的风格编写最强大/通用的 C++11 信号量(注意
using semaphore = ...
,您通常只需使用命名semaphore
类似于通常使用string
而不是basic_string
):I decided to write the most robust/generic C++11 semaphore I could, in the style of the standard as much as I could (note
using semaphore = ...
, you normally would just use the namesemaphore
similar to normally usingstring
notbasic_string
):C++20 终于有了信号量 -
std::counting_semaphore
。它们(至少)具有以下方法:
acquire()
(阻塞)try_acquire()
(非阻塞,立即返回)try_acquire_for()
(非阻塞,需要一段时间)try_acquire_until()
(非阻塞,需要一段时间才能停止尝试)release()
您可以阅读 这些 CppCon 2019 演示幻灯片,或观看 视频。还有官方提案 P0514R4,但它可能不是实际的 C++20 的最新版本。
C++20 finally has semaphores -
std::counting_semaphore<max_count>
.These have (at least) the following methods:
acquire()
(blocking)try_acquire()
(non-blocking, returns immediately)try_acquire_for()
(non-blocking, takes a duration)try_acquire_until()
(non-blocking, takes a time at which to stop trying)release()
You can read these CppCon 2019 presentation slides, or watch the video. There's also the official proposal P0514R4, but it may not be up-to-date with actual C++20.
根据 posix 信号量,我想补充
一点,我更喜欢在方便的抽象级别使用同步机制,而不是总是使用更基本的运算符复制粘贴缝合在一起的版本。
in acordance with posix semaphores, I would add
And I much prefer using a synchronisation mechanism at a convenient level of abstraction, rather than always copy pasting a stitched-together version using more basic operators.
您还可以查看 cpp11-on-multicore - 它具有可移植且最佳的信号量实现。
该存储库还包含补充 c++11 线程的其他线程好东西。
You can also check out cpp11-on-multicore - it has a portable and optimal semaphore implementation.
The repository also contains other threading goodies that complement c++11 threading.
您可以使用互斥锁和条件变量。您获得互斥锁的独占访问权限,检查是否要继续或需要等待另一端。如果你需要等待,你就在一个条件下等待。当另一个线程确定您可以继续时,它会发出该条件信号。
有一个简短的示例 在 boost::thread 库中,您很可能只需复制它(C++0x 和 boost 线程库非常相似)。
You can work with mutex and condition variables. You gain exclusive access with the mutex, check whether you want to continue or need to wait for the other end. If you need to wait, you wait in a condition. When the other thread determines that you can continue, it signals the condition.
There is a short example in the boost::thread library that you can most probably just copy (the C++0x and boost thread libs are very similar).
线程中的 RAII 信号量包装器也很有用:
多线程应用程序中的使用示例:
Also can be useful RAII semaphore wrapper in threads:
Usage example in multithread app:
我发现shared_ptr和weak_ptr,一个很长的列表,完成了我需要的工作。我的问题是,我有几个客户端想要与主机的内部数据进行交互。通常,主机会自行更新数据,但是,如果客户端请求,则主机需要停止更新,直到没有客户端访问主机数据为止。同时,客户端可以请求独占访问,以便其他客户端或主机都无法修改该主机数据。
我是如何做到这一点的,我创建了一个结构:
每个客户端都会有一个这样的成员:
然后主机将有一个用于排他性的weak_ptr成员,以及一个用于非排他锁的weak_ptr列表:
有一个启用锁定的函数,另一个函数检查主机是否被锁定:
我在 LockUpdate、IsUpdateLocked 中测试锁定,并定期在主机的 Update 例程中测试锁定。测试锁就像检查weak_ptr是否过期一样简单,并从m_locks列表中删除任何过期的内容(我只在主机更新期间执行此操作),我可以检查列表是否为空;同时,当客户端重置它们所挂载的shared_ptr时,我会自动解锁,当客户端自动销毁时也会发生这种情况。
总体效果是,由于客户端很少需要独占性(通常仅保留用于添加和删除),因此大多数情况下,对 LockUpdate( false ) 的请求(也就是说非独占性)只要 (! m_exclusiveLock) 就会成功。并且只有 (! m_exclusiveLock) 和 (m_locks.empty()) 两者都存在时,LockUpdate( true )(独占性请求)才会成功。
可以添加一个队列来缓解排他锁和非排他锁之间的冲突,但是,到目前为止我还没有发生冲突,所以我打算等到这种情况发生后再添加解决方案(主要是这样我有一个真实的测试条件)。
到目前为止,这可以很好地满足我的需求;我可以想象需要扩展它,以及扩展使用时可能出现的一些问题,但是,它实现起来很快,并且需要很少的自定义代码。
I found the shared_ptr and weak_ptr, a long with a list, did the job I needed. My issue was, I had several clients wanting to interact with a host's internal data. Typically, the host updates the data on it's own, however, if a client requests it, the host needs to stop updating until no clients are accessing the host data. At the same time, a client could ask for exclusive access, so that no other clients, nor the host, could modify that host data.
How I did this was, I created a struct:
Each client would have a member of such:
Then the host would have a weak_ptr member for exclusivity, and a list of weak_ptrs for non-exclusive locks:
There is a function to enable locking, and another function to check if the host is locked:
I test for locks in LockUpdate, IsUpdateLocked, and periodically in the host's Update routine. Testing for a lock is as simple as checking if the weak_ptr's expired, and removing any expired from the m_locks list (I only do this during the host update), I can check if the list is empty; at the same time, I get automatic unlocking when a client resets the shared_ptr they are hanging onto, which also happens when a client gets destroyed automatically.
The over all effect is, since clients rarely need exclusivity (typically reserved for additions and deletions only), most of the time a request to LockUpdate( false ), that is to say non-exclusive, succeeds so long as (! m_exclusiveLock). And a LockUpdate( true ), a request for exclusivity, succeeds only when both (! m_exclusiveLock) and (m_locks.empty()).
A queue could be added to mitigate between exclusive and non-exclusive locks, however, I have had no collisions thus far, so I intend to wait until that happens to add the solution (mostly so I have a real-world test condition).
So far this is working well for my needs; I can imagine the need to expand this, and some issues that might arise over expanded use, however, this was quick to implement, and required very little custom code.
与其他答案不同,我提出了一个新版本:
wait()
调用的参数,用于在超时(以毫秒为单位)过后自动解锁调用线程。notify()
次数过多不会增加信号量拥有的资源数量。打印当前时间戳的实用程序:
使用此信号量的示例程序:
示例输出:
使用 EventLoop 解锁信号量的额外函数一段时间后:
Different from other answers, I propose a new version which:
wait()
call, to automatically unlock the calling thread after the timeout in milliseconds has passed.notify()
too many times will not increase how many resources the semaphore has.Utility to print the current timestamp:
Example program using this semaphore:
Example output:
Extra function which uses a EventLoop to unlock the semaphores after some time:
这是一个老问题,但我想提供另一个解决方案。
看来您需要的不是信号量,而是像 Windows Events 这样的事件。
可以像下面这样完成非常有效的事件:
然后只需使用 并发::事件
There old question but I would like to offer another solution.
It seems you need a not semathore but a event like Windows Events.
Very effective events can be done like following:
And then just use concurrency::event
如果有人对原子版本感兴趣,这里是实现。预计性能会比互斥锁和互斥锁更好。条件变量版本。
In case someone is interested in the atomic version, here is the implementation. The performance is expected better than the mutex & condition variable version.