排队的线程通知

时间:2015-05-12 15:19:52

标签: c++ multithreading c++11

你可以想象我的问题我描述了我的设计的用法:

在类SerialInterface中,如果收到消息,则每隔10ms检查一个线程。该类实现为Observer模式,以通知其他类有关新接收的消息/字节。

Observer模式的Notify方法阻塞,直到每个主题都完成了它的操作。因为我想避免任何滞后,我想异步通知对象。

我的第一个想法是事件(C ++ 11中的条件变量)。

实现如下:

class SerialInterface: public Observer {
private:
  .....
    void NotifyThread() {
        while (mRunThreadNotify) {
            std::unique_lock<std::mutex> lock(mMutex);
            mCv.wait(lock);

            NotifyObservers();
        }
    }

    std::mutex              mMutex;
    std::condition_variable mCv;
    std::atomic_bool        mRunThreadNotify;
    std::thread             mThreadNotify;
  .....
};

现在我可以通过mCv.notify_all();

异步通知

现在的问题是:

如果线程NotifyThread()当前正在通知主题,但是同时传入新的通知事件,该怎么办?它将完成当前通知,并且将跳过新状态。

所以我的第二种方法是创建一个通知计数器,让它像一个队列:

class SerialInterface: public Observer {
public:
  ....
private:
  .....
    void NotifyThread() {
        while (mRunThreadNotify) {
            if (mNotifications > 0) {
                NotifyObservers();
                mNotifications--;
            } else {
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
        }
    }

    std::atomic<size_t> mNotifications;
    std::atomic_bool    mRunThreadNotify;
    std::thread         mThreadNotify;
  .....
};

这里我必须增加变量mNotifications以通知主题。但对我来说,这个解决方案看起来并不完美,因为我使用std::this_thread::sleep_for进行固定的等待时间。

这个问题是否有任何建议或其他方法?

2 个答案:

答案 0 :(得分:2)

在我看来,您希望将实时行为(10mS串行轮询)与程序的其余部分分开,以便实时线程永远不会等待任何其他例程。鉴于此,我的建议是将模式分为两部分:

  1. 实时部分,它只接收传入的串行数据并将其附加到FIFO队列的末尾(当然是以线程安全的方式)。

  2. 非实时部分(在不同的线程中运行),其中数据从FIFO队列的头部弹出并传递给想要对其作出反应的所有软件组件。这部分可以像它喜欢的那样快或慢,因为它不会阻碍实时线程。

  3. FIFO队列部分是标准的生产者 - 消费者问题;有多种方法可以实现它,但我通常使用的方法是使用dequeue,lock和条件变量(伪代码):

    // Called by the real-time/serial thread when it received serial data
    void AppendBytesToQueue(const TheBytesObject & bytes)
    {
       bool wasQueueEmptyBefore;
       m_lock.lock();
       wasQueueEmptyBefore = (m_fifo.size() == 0);
       m_fifo.push_back(bytes);
       m_lock.unlock();
       if (wasQueueEmptyBefore) m_condition_variable.signal();
    }
    
    // Called by the non-real-time/handling thread after it was 
    // woken up by the condition variable's signal (outQueue should
    // be a reference to an empty dequeue that gets filled by this
    // method)
    void GetNewBytesFromQueue(std::dequeue & outQueue)
    {
       m_lock.lock();
       std::swap(m_fifo, outQueue);  // fast O(1) operation so m_lock() will never be locked for long
       m_lock.unlock();
    }
    

    ...然后在调用GetNewBytesFromQueue()之后,处理/非实时线程可以遍历其临时出队的内容并按顺序处理每个项目,而不会有任何影响串行线程性能的风险。

答案 1 :(得分:1)

收到通知后,您可以检查当时是否符合要求。

满足要求可以在wait()的第二个参数中指定为谓词。

mCvNotifications.wait(lock, [](){return true_if_requirements_met;}); 

如果未满足要求,尽管有通知,线程仍将处于等待阶段。