C++ 在C++;11?
首先是一个小的上下文:我正在学习C++11中的线程,为此,我尝试构建一个小的C++ 在C++;11?,c++,multithreading,c++11,synchronization,condition-variable,C++,Multithreading,C++11,Synchronization,Condition Variable,首先是一个小的上下文:我正在学习C++11中的线程,为此,我尝试构建一个小的actor类,基本上(我忽略了异常处理和传播的内容),如下所示: 简而言之,问题是这样的:new\u msg\u通知中如何中断等待新消息的过程。当触发中断时(不引入超时),等待(…) 或者,问题可以理解为:如何等待两个std::condition_变量s中的任何一个发出信号 一种简单的方法似乎是根本不使用std::condition_variable作为中断,而是使用原子标志std::atomic interrupted
actor
类,基本上(我忽略了异常处理和传播的内容),如下所示:
简而言之,问题是这样的:new\u msg\u通知中如何中断等待新消息的过程。当触发中断时(不引入超时),等待(…)
或者,问题可以理解为:如何等待两个std::condition_变量s中的任何一个发出信号
一种简单的方法似乎是根本不使用std::condition_variable
作为中断,而是使用原子标志std::atomic interrupted
,然后忙着等待new_msg_通知
,直到新消息到达或true==interrupted
。不过,我很想避免忙碌的等待
编辑:
从皮尔克罗的评论和回答来看,基本上有两种可能的方法
按照Alan、mukunda和pilcrow的建议,将一条特殊的“终止”消息排队。我决定不使用这个选项,因为我不知道在我希望参与者终止时队列的大小。很可能(当我想快速终止某个消息时,大多数情况下都是这样)队列中还有数千条消息要处理,等待它们被处理,直到最终轮到终止消息为止,这似乎是不可接受的
实现条件变量的自定义版本,通过将第一个线程正在等待的通知转发给条件变量,可能会被另一个线程中断。我选择了这种方法
对于那些感兴趣的人,我的实现如下。在我的例子中,条件变量实际上是一个信号量(因为我更喜欢它们,也因为我喜欢这样做的练习)。我为这个信号量配备了一个相关的中断
,可以通过semaphore::get_interrupt()
从信号量获取该中断。如果现在一个线程阻塞了semaphore::wait()
,那么另一个线程就有可能在信号量中断时调用semaphore::interrupt::trigger()
,导致第一个线程取消阻塞并传播interrupt\u异常
struct
interrupt_exception {};
class
semaphore {
public: class interrupt;
private: mutable std::mutex mutex;
// must be declared after our mutex due to construction order!
private: interrupt* informed_by;
private: std::atomic<long> counter;
private: std::condition_variable cond;
public:
semaphore();
public:
~semaphore() throw();
public: void
wait();
public: interrupt&
get_interrupt() const { return *informed_by; }
public: void
post() {
std::lock_guard<std::mutex> lock(mutex);
counter++;
cond.notify_one(); // never throws
}
public: unsigned long
load () const {
return counter.load();
}
};
class
semaphore::interrupt {
private: semaphore *forward_posts_to;
private: std::atomic<bool> triggered;
public:
interrupt(semaphore *forward_posts_to) : triggered(false), forward_posts_to(forward_posts_to) {
assert(forward_posts_to);
std::lock_guard<std::mutex> lock(forward_posts_to->mutex);
forward_posts_to->informed_by = this;
}
public: void
trigger() {
assert(forward_posts_to);
std::lock_guard<std::mutex>(forward_posts_to->mutex);
triggered = true;
forward_posts_to->cond.notify_one(); // never throws
}
public: bool
is_triggered () const throw() {
return triggered.load();
}
public: void
reset () throw() {
return triggered.store(false);
}
};
semaphore::semaphore() : counter(0L), informed_by(new interrupt(this)) {}
// must be declared here because otherwise semaphore::interrupt is an incomplete type
semaphore::~semaphore() throw() {
delete informed_by;
}
void
semaphore::wait() {
std::unique_lock<std::mutex> lock(mutex);
if(0L==counter) {
cond.wait(lock,[&]{
if(informed_by->is_triggered())
throw interrupt_exception();
return counter>0;
});
}
counter--;
}
我的actor
,现在能够以非常低的线程延迟中断其线程。目前的实施情况如下:
class
actor {
private: message_queue
incoming_msgs;
/// must be declared after incoming_msgs due to construction order!
private: semaphore::interrupt&
interrupt;
private: std::thread
my_thread;
private: std::exception_ptr
exception;
public:
actor()
: interrupt(incoming_msgs.get_interrupt()), my_thread(
[&]{
try {
run_actor();
}
catch(...) {
exception = std::current_exception();
}
})
{}
private: virtual void
run_actor() {
while(!interrupt.is_triggered())
process(incoming_msgs.wait_and_pop());
};
private: virtual void
process(const message&) = 0;
public: void
notify(message&& msg_in) {
incoming_msgs.push(std::forward<message>(msg_in));
}
public: virtual
~actor() throw (interrupt_exception) {
interrupt.trigger();
my_thread.join();
if(exception)
std::rethrow_exception(exception);
}
};
类
演员{
专用:消息队列
输入的MSG;
///由于施工订单的原因,必须在进货后申报!
私有:信号量::中断&
打断
私有:std::thread
我的线;
私有:std::异常\u ptr
例外情况;
公众:
演员()
:中断(传入的\u msgs.get\u中断()),我的\u线程(
[&]{
试一试{
run_actor();
}
捕获(…){
异常=标准::当前_异常();
}
})
{}
私有:虚拟无效
run_actor(){
而(!interrupt.is_triggered())
进程(传入的_msgs.wait_和_pop());
};
私有:虚拟无效
进程(常量消息&)=0;
公众:无效
通知(消息中的消息和消息){
传入消息推送(std::forward(msg_in));
}
公众:虚拟
~actor()抛出(中断\异常){
interrupt.trigger();
我的_线程。join();
如果(例外)
std::重新计时异常(异常);
}
};
你问
在C++11中,等待多个条件变量的最佳方式是什么
你不能,而且必须重新设计。一个线程一次只能等待一个条件变量(及其关联的互斥体)。在这方面,Windows的同步功能比“POSIX风格”的同步原语系列更加丰富
使用线程安全队列的典型方法是将一条特殊的“全部完成!”消息排入队列,或者设计一个“可中断”(或“可关闭”)队列。在后一种情况下,队列的内部条件变量保护一个复杂谓词:要么项目可用,要么队列已中断
你在评论中注意到
如果没有人等待,则notify_all()将无效
这是事实,但可能与此无关wait()
ing条件变量还意味着检查谓词,并在实际阻止通知之前进行检查。因此,正在处理“未命中”队列项目的工作线程在下次检查队列条件时将看到谓词(新项目可用,或者队列已全部完成)已更改。最近,我在单个条件变量和每个生产者/工作者的单独布尔变量的帮助下解决了此问题。
使用者线程中的wait函数中的谓词可以检查这些标志,并决定哪个生产者/工作者满足了条件 也许这能起作用:
摆脱干扰
message wait_and_pop(std::condition_variable& interrupt) {
std::unique_lock<std::mutex> lock(mutex);
{
new_msg_notification.wait(lock,[&]{
return !queue.empty() || stop;
});
if( !stop )
{
auto msg(std::move(queue.front()));
queue.pop();
return msg;
}
else
{
return NULL; //or some 'terminate' message
}
}
消息等待和弹出(std::条件变量和中断){
std::唯一锁(互斥锁);
{
新建消息通知。等待(锁定,[&]{
return!queue.empty()| |停止;
});
如果(!停止)
{
自动消息(std::move(queue.front());
queue.pop();
返回味精;
}
其他的
{
返回NULL;//或某些“终止”消息
}
}
在析构函数中,将中断替换为new\u msg\u通知。notify\u all()
class
message_queue {
private: std::queue<message> queue;
private: semaphore new_msg_notification;
public: void
push(message&& msg) {
queue.push(std::move(msg));
new_msg_notification.post();
}
public: const message
wait_and_pop() {
new_msg_notification.wait();
auto msg(std::move(queue.front()));
queue.pop();
return msg;
}
public: semaphore::interrupt&
get_interrupt() const { return new_msg_notification.get_interrupt(); }
};
class
actor {
private: message_queue
incoming_msgs;
/// must be declared after incoming_msgs due to construction order!
private: semaphore::interrupt&
interrupt;
private: std::thread
my_thread;
private: std::exception_ptr
exception;
public:
actor()
: interrupt(incoming_msgs.get_interrupt()), my_thread(
[&]{
try {
run_actor();
}
catch(...) {
exception = std::current_exception();
}
})
{}
private: virtual void
run_actor() {
while(!interrupt.is_triggered())
process(incoming_msgs.wait_and_pop());
};
private: virtual void
process(const message&) = 0;
public: void
notify(message&& msg_in) {
incoming_msgs.push(std::forward<message>(msg_in));
}
public: virtual
~actor() throw (interrupt_exception) {
interrupt.trigger();
my_thread.join();
if(exception)
std::rethrow_exception(exception);
}
};
message wait_and_pop(std::condition_variable& interrupt) {
std::unique_lock<std::mutex> lock(mutex);
{
new_msg_notification.wait(lock,[&]{
return !queue.empty() || stop;
});
if( !stop )
{
auto msg(std::move(queue.front()));
queue.pop();
return msg;
}
else
{
return NULL; //or some 'terminate' message
}
}