Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/150.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在C++;11?_C++_Multithreading_C++11_Synchronization_Condition Variable - Fatal编程技术网

C++ 在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

首先是一个小的上下文:我正在学习C++11中的线程,为此,我尝试构建一个小的
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
            }
    }