你可以想象我的问题我描述了我的设计的用法:
在类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
进行固定的等待时间。
这个问题是否有任何建议或其他方法?
答案 0 :(得分:2)
在我看来,您希望将实时行为(10mS串行轮询)与程序的其余部分分开,以便实时线程永远不会等待任何其他例程。鉴于此,我的建议是将模式分为两部分:
实时部分,它只接收传入的串行数据并将其附加到FIFO队列的末尾(当然是以线程安全的方式)。
非实时部分(在不同的线程中运行),其中数据从FIFO队列的头部弹出并传递给想要对其作出反应的所有软件组件。这部分可以像它喜欢的那样快或慢,因为它不会阻碍实时线程。
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;});
如果未满足要求,尽管有通知,线程仍将处于等待阶段。