用观察者模式通知UI刷新数据

用观察者模式用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();
}
相关推荐
CoderCodingNo3 小时前
【GESP】C++四级真题 luogu-B4040 [GESP202409 四级] 黑白方块
开发语言·c++
小欣加油4 小时前
leetcode 143 重排链表
数据结构·c++·算法·leetcode·链表
给大佬递杯卡布奇诺4 小时前
FFmpeg 基本API avio_open函数内部调用流程分析
c++·ffmpeg·音视频
爱吃生蚝的于勒5 小时前
【Linux】深入理解进程(一)
java·linux·运维·服务器·数据结构·c++·蓝桥杯
chuyanghong5 小时前
Ubuntu下VIM安装及配置
c++
boss-dog5 小时前
崩溃信息追溯——backward-cpp
c++·debug·backward-cpp
Hankin_Liu的技术研究室6 小时前
深入理解 C++ happens-before:高级并发程序员的必修课
c++
liu****6 小时前
20.哈希
开发语言·数据结构·c++·算法·哈希算法
爱和冰阔落7 小时前
【C++多态】虚函数/虚表机制与协变 、override和final关键字全解析
开发语言·c++·面试·腾讯云ai代码助手