【C++】POCO学习总结(八):通知Notifications和事件Events

【C++】郭老二博文之:C++目录

1、Notifications和Events的区别

1)通知Notifications:如果观察者不知道或不关心事件的来源,则使用通知Notifications。
Poco::NotificationCenter或Poco::NotificationQueue位于源source和目标target之间,并将它们解耦。
通知Notifications可以跨线程发送。

2)事件Events:如果观察者确实关心事件的源source,或者希望仅从特定源source接收事件,则使用事件Events。
事件还支持异步通知和一些Notifications不支持的特性。

3)Notifications和Events的异同
在这里插入图片描述
4)Notifications通知的调度流程
在这里插入图片描述
5)Events事件的调度流程
在这里插入图片描述

2、Notifications 通知

2.1 Poco::Notification

通知类派生自Poco::Notification并支持引用计数(与Poco::AutoPtr兼容)。
通知对象可以保存任意数据并提供任意操作。但是不支持没有复制构造函数、没有赋值函数的值语义,并且总是在堆上创建。

2.2 什么是“值语义”?

有值语义的变量,变量赋值可以转换成内存的逐位复制(bit-wise-copy)。
关于“值语义”请参考博客:https://www.cnblogs.com/ly8838/p/3929025.html

2.3 Poco::NotificationCenter

Poco::NotificationCenter是通知对象的调度程序。
头文件:#include “Poco/NotificationCenter.h”
Poco::NotificationCenter使用观察者对象(Poco::AbstractObserver的子类)告诉它的目标

2.4 订阅subscribe

目标可以通过使用成员函数addObserver()向NotificationCenter注册自己来订阅通知。
或者:使用NotificationCenter::addObserver(const AbstractObserver& observer)注册一个通知目标

订阅可以通过调用removeObserver()来取消。
或者:使用NotificationCenter::removeObserver(const AbstractObserver& observer)取消注册通知目标

2.5 观察者Observer

观察者对象存储一个指向目标对象的指针,以及一个指向目标对象的回调成员函数的指针,并且知道目标对哪些通知感兴趣。

观察者使用Observer或NObserver类模板。
1)对于Observer,接收回调的目标成员函数必须定义为:

void someCallback(SomeNotification* pNf)

其中someecallback可以是任何名称,而SomeNotification是要注册的通知。
回调获得通知对象的共享所有权,并且必须在不再需要它时释放它。

2)对于NObserver,目标成员函数为:

void someCallback(const AutoPtr<SomeNotification>& pNf)

2.6 回调函数Callback

在回调过程中,回调函数可以从NotificationCenter注销自己,或者向NotificationCenter注册新的回调。

在通知期间添加的观察器将在下一次通知时第一次调用。
在通知期间被移除的观察者将不会收到当前通知(除非它们已经收到通知)。

2.7 发布通知

通知由 NotificationCenter::postNotification() 方法发布以供调度。
void postNotification(Notification::Ptr pNotification)向所有订阅了通知类的目标发送通知。
通知下发到所有已注册的目标器。如果目标在处理通知时抛出异常,则调度停止,并将异常传播给调用方。
NotificationCenter拥有通知的所有权。

2.8 多态性

订阅特定通知类的目标也会接收作为该类子类的通知。

如果目标订阅了Poco::Notification,那么它将接收发送到它已注册的NotificationCenter的所有通知。

2.9 示例

$ vi observer.cpp

#include "Poco/NotificationCenter.h"
#include "Poco/Notification.h"
#include "Poco/Observer.h"
#include "Poco/NObserver.h"
#include "Poco/AutoPtr.h"
#include <iostream>

using Poco::NotificationCenter;
using Poco::Notification;
using Poco::Observer;
using Poco::NObserver;
using Poco::AutoPtr;

class BaseNotification: public Notification 
{
public:
	int id = 101;
};

class SubNotification: public BaseNotification 
{
public:
	int id = 202;
};

class Target
{
public:
	void handleBase(BaseNotification* pNf)
	{
		std::cout << "handleBase: " << pNf->name() << "; id = " << pNf->id << std::endl;
		pNf->release(); // we got ownership, so we must release
	}

	void handleSub(const AutoPtr<SubNotification>& pNf)
	{
		std::cout << "handleSub: " << pNf->name() << "; id = " << pNf->id << std::endl;
	}
};

int main(int argc, char** argv)
{
	NotificationCenter nc;
	Target target;
	nc.addObserver(Observer<Target, BaseNotification>(target, &Target::handleBase));
	nc.addObserver(NObserver<Target, SubNotification>(target, &Target::handleSub));
	
	// 注意:在这里new的,将会在handleBase()中pNf->release()
	nc.postNotification(new BaseNotification);
	nc.postNotification(new SubNotification);
	
	nc.removeObserver(Observer<Target, BaseNotification>(target, &Target::handleBase));
	nc.removeObserver(NObserver<Target, SubNotification>(target, &Target::handleSub));
	return 0;
}

编译:

g++ observer.cpp -I ~/git/poco/install/include -L ~/git/poco/install/lib -lPocoFoundationd -lpthread

输出 ;

$ ./a.out 
handleBase: 16BaseNotification; id = 101
handleBase: 15SubNotification; id = 101
handleSub: 15SubNotification; id = 202

3、Poco::NotificationQueue异步通知

3.1 说明

Poco::NotificationQueue可用于从一个线程异步地向另一个线程发送通知。
头文件:#include “Poco/NotificationQueue.h”
多个线程可以从NotificationQueue中读取数据。

使用NotificationQueue来:

  • 从后台处理线程向用户界面线程发送通知,
  • 从控制线程向一个或多个工作线程发送通知。

在这里插入图片描述
在这里插入图片描述

3.2 入队

void enqueueNotification(Notification::Ptr pNotification)

通知添加到队列的末尾(先进先出 FIFO 原则)。队列获得通知的所有权。

void enqueueUrgentNotification(Notification::Ptr pNotification)

通知添加到队列的开头(后进先出 LIFO 原则)。队列获得通知的所有权。

3.3 出队

Notification* dequeueNotification()

将下一个挂起的通知从队列的开头取出,如果没有可用的通知,则为空。调用方获得通知的所有权。

Notification* waitDequeueNotification()
Notification* waitDequeueNotification(long timeout)

如果没有可用的通知,则等待(最多超时timeout毫秒)发布通知。返回通知,如果没有,则返回null。

3.4 通知完成

如何告诉其它工作线程队列关闭了?

  • 为每个工作线程发送一个特殊的QuitNotification;
  • 设置一个(全局)停止标志,并使用waitDequeueNotification()带超时;
  • 使用wakeUpAll():每次调用waitDequeueNotification()都会立即返回null。

3.5 示例

$ vi notificationQueue.cpp

#include "Poco/Notification.h"
#include "Poco/NotificationQueue.h"
#include "Poco/ThreadPool.h"
#include "Poco/Runnable.h"
#include "Poco/AutoPtr.h"
#include "iostream"

using Poco::Notification;
using Poco::NotificationQueue;
using Poco::ThreadPool;
using Poco::Runnable;
using Poco::AutoPtr;

class WorkNotification: public Notification
{
public:
	WorkNotification(int data): _data(data) {}
	int data() const { return _data; }
private:
	int _data;
};

class Worker: public Runnable
{
public:
	Worker(NotificationQueue& queue): _queue(queue) {}
	void run()
	{
		AutoPtr<Notification> pNf(_queue.waitDequeueNotification());
		while (pNf)
		{
			WorkNotification* pWorkNf = dynamic_cast<WorkNotification*>(pNf.get());
			if (pWorkNf)
			{
				std::cout << "hello world!" << std::endl;
			}
			pNf = _queue.waitDequeueNotification();
		}
	}
private:
	NotificationQueue& _queue;
};

int main(int argc, char** argv)
{
	NotificationQueue queue;
	Worker worker1(queue); // create worker threads
	Worker worker2(queue);
	ThreadPool::defaultPool().start(worker1); // start workers
	ThreadPool::defaultPool().start(worker2);
	
	for (int i = 0; i < 100; ++i)
	{
		queue.enqueueNotification(new WorkNotification(i));
	}
	while (!queue.empty()) // wait until all work is done
		Poco::Thread::sleep(100);
		
	queue.wakeUpAll(); // tell workers they're done
	ThreadPool::defaultPool().joinAll();
	return 0;
}

编译

g++ notificationQueue.cpp -I ~/git/poco/install/include -L ~/git/poco/install/lib -lPocoFoundationd -lpthread

3.6 特殊队列

1)带优先级的队列
Poco::PriorityNotificationQueue
通知时,标记优先级,并按其优先级顺序出队列(数值越低意味着优先级越高)。

2)带时间戳的队列
Poco::TimedNotificationQueue
通知时,标记时间戳,并按照时间戳的顺序出队列

4、Events 事件

4.1 说明

POCO中的事件以c#事件为模型,使用c++方式来实现。
与通知Notifications相反,Events事件是类接口的一部分。
事件支持异步通知、不同的通知策略和自动过期。

4.2 用法

1)定义事件
头文件:#include “Poco/BasicEvent.h”

事件使用Poco::BasicEvent类模板定义。
使用事件参数的类型实例化Poco::BasicEvent
通常,将事件作为公共数据成员添加到类中。

2)委托
头文件:#include “Poco/Delegate.h”
目标通过使用Poco::Delegate类模板注册来订阅事件,向事件注册回调成员函数。
委托是使用事件的+=操作符向事件注册的,类似地,使用-=操作符注销委托。

3)参数
头文件:#include “Poco/EventArgs.h”
一个事件只有一个参数,它可以是一个子类Poco::EventArgs

4)回调函数
与委托一起使用的回调函数必须是具有以下签名之一的函数:

void handler(const void* pSender, EventArg& arg)

第一个参数pSender:指向触发事件的对象
第二个参数arg:是对传递事件的引用。

回调函数可以修改事件参数(除非它已被声明为const)以将数据传递回发送方

5)发射事件
事件可以通过调用其notify()成员函数来同步触发
事件可以通过调用notifyAsync()成员函数来异步触发
如果任何事件处理程序抛出异常,则事件调度立即停止,并将异常传播给调用者。

4.3 示例

$ vi event1.cpp

#include "Poco/BasicEvent.h"
#include "Poco/Delegate.h"
#include <iostream>

using Poco::BasicEvent;
using Poco::Delegate;

class Source
{
public:
	BasicEvent<int> theEvent;
	void setId(int id){m_id = id;}
	int getId(){return m_id;};
	void fireEvent()
	{
		theEvent(this, m_id);
		// theEvent.notify(this, n); // alternative syntax
	}

private:
	int m_id = 0;
};

class Target
{
public:
	void onEvent(const void* pSender, int& arg)
	{
		std::cout << "onEvent: " << arg << std::endl;
		arg = 1987;
	}
};

int main(int argc, char** argv)
{
	Source source;
	Target target;
	source.theEvent += Poco::delegate(&target, &Target::onEvent);
	source.setId(9527);
	source.fireEvent();
	std::cout << "source id = " << source.getId() << std::endl;
	source.theEvent -= Poco::delegate(&target, &Target::onEvent);
	return 0;
}

编译:

$ g++ event1.cpp -I ~/git/poco/install/include -L ~/git/poco/install/lib -lPocoFoundationd -lpthread 

输出:

$ ./a.out 
onEvent, id = 9527
source id = 1987

4.4 同步事件与异步事件

当事件处理很快 或者 需要同步时,可以使用同步事件notify();
与互斥锁结合使用时,可能造成死锁;

4.5 注意事项

永远不要忘记取消注册委托!否则,悬空指针将在以后的通知中导致未定义的行为(崩溃)。
每个目标只能在一个事件中注册一个委托。如果目标用单个事件注册两个回调函数,则后一个回调函数将替换第一个回调函数。
取消注册从未注册或已经过期的委托是可以的。
事件是线程安全的,也就是说,你可以在通知正在进行时修改委托集。新委托集不会影响当前通知,但会随下一个通知生效

4.6 高级事件

1)Poco::FIFOEvent:可以确保以与添加委托相同的顺序调用委托

2)Poco::PriorityEvent为委托添加优先级
可以使用Poco::FIFOEvent来代替Poco::BasicEvent,以确保以与添加委托相同的顺序调用委托。
必须使用Poco::PriorityDelegate类模板添加委托。委托按其优先级顺序调用,优先级较低的先调用。

3)Poco::Expire:自动自动过期委托
使用Poco::Expire类模板作为Poco::Delegate的包装来定义。

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭老二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值