第三十四篇:在SOUI中使用异步通知

概述

异步通知是客户端开发中常见的需求,比如在一个网络处理线程中要通知UI线程更新等等。

通常在Windows编程中,为了方便,我们一般会向UI线程的窗口句柄Post/Send一个窗口消息从而达到将非UI线程的事件切换到UI线程处理的目的。

在SOUI引入通知中心以前要在SOUI中处理非UI线程事件我也推荐用上面的方法。

使用窗口消息至少有以下两个不足:

1、需要在线程中持有一个窗口句柄。

2、发出的消息只能在该窗口句柄的消息处理函数里处理。

SNotifyCenter

最新的SOUI引入了一个新的单例对象:SNotifyCenter,专门用来处理这类问题。

新的SNotifyCenter解决了窗口消息存在的上面的两个问题:

1、通过使用全局单例,SNotifyCenter可以在代码任意位置获取它的指针(前提当然是要在它的生命周期内);

2、使用SNotifyCenter产生的通知采用SOUI的事件系统来派发,通过结合SOUI的事件订阅系统,用户可以在任意位置处理发出的事件。

在介绍如何使用SNotifyCenter前,先看一下NotifyCenter.h的代码:

 1 #pragma once
 2 
 3 #include <core/SSingleton.h>
 4 
 5 namespace SOUI
 6 {
 7     template<class T>
 8     class TAutoEventMapReg
 9     {
10         typedef TAutoEventMapReg<T> _thisClass;
11     public:
12         TAutoEventMapReg()
13         {
14             SNotifyCenter::getSingleton().RegisterEventMap(Subscriber(&_thisClass::OnEvent,this));
15         }
16 
17         ~TAutoEventMapReg()
18         {
19             SNotifyCenter::getSingleton().UnregisterEventMap(Subscriber(&_thisClass::OnEvent,this));
20         }
21 
22     protected:
23         bool OnEvent(EventArgs *e){
24             T * pThis = static_cast<T*>(this);
25             return !!pThis->_HandleEvent(e);
26         }
27     };
28 
29     class SOUI_EXP SNotifyCenter : public SSingleton<SNotifyCenter>
30                         , public SEventSet
31     {
32     public:
33         SNotifyCenter(void);
34         ~SNotifyCenter(void);
35 
36         /**
37         * FireEventSync
38         * @brief    触发一个同步通知事件
39         * @param    EventArgs *e -- 事件对象
40         * @return    
41         *
42         * Describe  只能在UI线程中调用
43         */
44         void FireEventSync(EventArgs *e);
45 
46         /**
47         * FireEventAsync
48         * @brief    触发一个异步通知事件
49         * @param    EventArgs *e -- 事件对象
50         * @return    
51         *
52         * Describe  可以在非UI线程中调用,EventArgs *e必须是从堆上分配的内存,调用后使用Release释放引用计数
53         */
54         void FireEventAsync(EventArgs *e);
55 
56 
57         /**
58         * RegisterEventMap
59         * @brief    注册一个处理通知的对象
60         * @param    const ISlotFunctor &slot -- 事件处理对象
61         * @return    
62         *
63         * Describe 
64         */
65         bool RegisterEventMap(const ISlotFunctor &slot);
66 
67         /**
68         * RegisterEventMap
69         * @brief    注销一个处理通知的对象
70         * @param    const ISlotFunctor &slot -- 事件处理对象
71         * @return    
72         *
73         * Describe 
74         */
75         bool UnregisterEventMap(const ISlotFunctor & slot);
76     protected:
77         void OnFireEvent(EventArgs *e);
78 
79         void ExecutePendingEvents();
80 
81         static VOID CALLBACK OnTimer( HWND hwnd,
82             UINT uMsg,
83             UINT_PTR idEvent,
84             DWORD dwTime
85             );
86 
87         SCriticalSection    m_cs;            //线程同步对象
88         SList<EventArgs*>    *m_evtPending;//挂起的等待执行的事件
89         DWORD                m_dwMainTrdID;//主线程ID
90         
91         UINT_PTR            m_timerID;    //定时器ID,用来执行异步事件
92 
93         SList<ISlotFunctor*>    m_evtHandlerMap;
94     };
95 }

在这个文件中提供了两个类,一个就是SNotifyCenter,另一个是TAutoEventMapReg<T>。

可以看到SNotifyCenter中除构造外只有4个public方法: 

FireEventSync, FireEventAsync用来触发事件。

RegisterEventMap,UnregisterEventMap则用来提供事件处理订阅。

如何使用SNotifyCenter?

1、在main中实例化SNotifyCenter。(不明白可以参考demo)

2、定义需要通过通知中心分发的事件类型,首先定义事件,然后向通知中心注册,参见下面代码:

 1 void CMainDlg::OnBtnStartNotifyThread()
 2 {
 3     if(IsRunning()) return;
 4     SNotifyCenter::getSingleton().addEvent(EVENTID(EventThreadStart));
 5     SNotifyCenter::getSingleton().addEvent(EVENTID(EventThreadStop));
 6     SNotifyCenter::getSingleton().addEvent(EVENTID(EventThread));
 7 
 8     EventThreadStart evt(this);
 9     SNotifyCenter::getSingleton().FireEventSync(&evt);
10     BeginThread();    
11 }
12 
13 void CMainDlg::OnBtnStopNotifyThread()
14 {
15     if(!IsRunning()) return;
16 
17     EndThread();
18     EventThreadStop evt(this);
19     SNotifyCenter::getSingleton().FireEventSync(&evt);
20 
21     SNotifyCenter::getSingleton().removeEvent(EventThreadStart::EventID);
22     SNotifyCenter::getSingleton().removeEvent(EventThreadStop::EventID);
23     SNotifyCenter::getSingleton().removeEvent(EventThread::EventID);
24 }

3、使需要处理通知中心分发的事件的对象从TAutoEventMapReg继承,实现事件的自动订阅(方便在事件映射表中统一处理事件),这一步是可选的,你也可以直接使用SOUI提供的事件订阅机制向通知中心订阅特定事件。

4、在事件映射表里处理事件(没有第3步时,则同样没有这一步)。

具体用法参见SOUI的demo

原文地址:https://www.cnblogs.com/setoutsoft/p/5642056.html