用观察者模式用UI显示数据变化
- 主程序
main.cpp
#include "QtWidgetsApplication1.h"
#include <QtWidgets/QApplication>
#include "Observer.h"
#include <QDebug>
class UIObserver : public Observer {
public:
void Execute() override { qDebug() << "[UI] refresh"; }
};
class IoObserver : public Observer {
public:
void Execute() override
{
qDebug() << "[IO] flush";
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
auto& m = Model::GetInstance();
std::shared_ptr<QtWidgetsApplication1> window = std::make_shared<QtWidgetsApplication1>();
std::shared_ptr<UIObserver> UI = std::make_shared<UIObserver>();
std::shared_ptr<IoObserver> OI = std::make_shared<IoObserver>();
int64_t h1 = m.Register(UI);
int64_t h2 = m.Register(OI);
int64_t h3 = m.Register(window);
window->show();
return app.exec();
}
- 逻辑UI
QtWidgetsApplication.h
#pragma once
#include <QtWidgets/QWidget>
#include "ui_QtWidgetsApplication1.h"
#include "Observer.h"
class QtWidgetsApplication1 : public QWidget, public Observer
{
Q_OBJECT
public:
QtWidgetsApplication1(QWidget *parent = nullptr);
~QtWidgetsApplication1();
void Execute() override;
public slots:
void onUpdate();
void doUpdate(int v); // 在主线程执行
private:
Ui::QtWidgetsApplication1Class ui;
int m_nCounter = 1;
};
QtWidgetsApplication.cpp
#include "QtWidgetsApplication1.h"
#include <QPushButton>
#include <QThread>
#include <QDebug>
QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
connect(ui.btnUpdate, &QPushButton::clicked, this, &QtWidgetsApplication1::onUpdate);
qDebug() << "Main thread ID: " << QThread::currentThreadId();
}
QtWidgetsApplication1::~QtWidgetsApplication1()
{}
void QtWidgetsApplication1::onUpdate()
{
Model::GetInstance().Notify();
}
void QtWidgetsApplication1::Execute()
{
qDebug() << "Current thread ID: " << QThread::currentThreadId();
if (thread() != QThread::currentThread())
{
QMetaObject::invokeMethod(this,
"doUpdate",
Qt::QueuedConnection,
Q_ARG(int, m_nCounter++));
}
else
{
doUpdate(m_nCounter++);
}
}
void QtWidgetsApplication1::doUpdate(int v)
{
if (thread() != QThread::currentThread())
{
qFatal("Execute is NOT in GUI thread!");
}
ui.lblValue->setText(QString::number(v));
}
- 模式设计
Observer.h
#pragma once
#include <queue>
#include <mutex>
#include <thread>
#include <memory>
#include <vector>
class Observer
{
public:
virtual ~Observer() = default;
virtual void Execute() = 0;
};
using ObserverPtr = std::shared_ptr<Observer>;
using WeakObserverPtr = std::weak_ptr<Observer>;
class Model
{
public:
virtual ~Model();
static Model& GetInstance()
{
static Model model;
return model;
}
/*-----增*删*改*查-----*/
int64_t Register(ObserverPtr sp); // 返回句柄,用于注销
void Unregister(int64_t handle); // 删除
void Notify(); // 异步通知(非阻塞)
void NotifySync(); // 同步通知
private:
Model();
struct Slot
{
int64_t handle;
WeakObserverPtr wptr;
};
std::vector<Slot> m_slots;
std::mutex m_mtx;
int64_t m_nextHandle = 1;
/*-----异步通知线程-----*/
void Worker();
std::thread m_worker;
std::queue<int64_t> m_taskQueue;
std::condition_variable m_cv;
bool m_stop = false;
};
Observer.cpp
#include "Observer.h"
#include <iostream>
int64_t Model::Register(ObserverPtr sp)
{
std::lock_guard<std::mutex> lock(m_mtx);
m_slots.push_back({ m_nextHandle, sp });
return m_nextHandle++;
}
void Model::Unregister(int64_t handle)
{
std::lock_guard<std::mutex> lock(m_mtx);
m_slots.erase(std::remove_if(m_slots.begin(), m_slots.end(),
[=](const Slot& s) {return s.handle == handle; }),m_slots.end());
}
// 同步通知(立即执行,会阻塞调用者)
void Model::NotifySync()
{
std::lock_guard<std::mutex> lock(m_mtx);
for (auto it = m_slots.begin(); it != m_slots.end();)
{
if (auto sp = it->wptr.lock())
{
sp->Execute();
++it;
}
else
{
it = m_slots.erase(it);
}
}
}
// 异步通知(把任务抛给后台线程,调用者立即返回)
void Model::Notify()
{
{
std::lock_guard<std::mutex> lock(m_mtx);
m_taskQueue.push(-1); // -1 代表"通知全部"
}
m_cv.notify_one();
}
// 后台worker真正遍历并调用
void Model::Worker()
{
while (true)
{
std::unique_lock<std::mutex> lock(m_mtx);
m_cv.wait(lock, [this] {return !m_taskQueue.empty() || m_stop; });
if (m_stop)
{
break;
}
auto todo = std::move(m_taskQueue);
lock.unlock();
while (!todo.empty())
{
int64_t cmd = todo.front();
todo.pop();
lock.lock();
for (auto& s : m_slots)
{
if (auto sp = s.wptr.lock())
{
sp->Execute();
}
}
lock.unlock();
}
}
}
Model::Model() :
m_worker(&Model::Worker, this) {}
Model::~Model()
{
{
std::lock_guard<std::mutex> lock(m_mtx);
m_stop = true;
m_cv.notify_all();
}
m_worker.join();
}