用观察者模式通知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();
}
相关推荐
卡提西亚3 小时前
C++笔记-25-函数模板
c++·笔记·算法
R&L_201810014 小时前
C++之内联变量(Inline Variables)
c++·c++新特性
IT阳晨。5 小时前
【QT开发】交叉编译QT程序在ARMLinux平台上运行
c++·qt·交叉编译·armlinux·代码移植
派大星爱吃猫5 小时前
C++隐藏的this指针(详解)
c++·this指针
虾..6 小时前
C++ 哈希
开发语言·c++·哈希算法
liu****6 小时前
14.日志封装和线程池封装
linux·开发语言·c++
将编程培养成爱好6 小时前
C++ 设计模式《统计辅助功能》
开发语言·c++·设计模式·访问者模式
一匹电信狗7 小时前
【C++】封装红黑树实现map和set容器(详解)
服务器·c++·算法·leetcode·小程序·stl·visual studio
wxin_VXbishe9 小时前
springboot在线课堂教学辅助系统-计算机毕业设计源码07741
java·c++·spring boot·python·spring·django·php
夕泠爱吃糖9 小时前
template关键字
开发语言·c++·template