扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
在面向对象的系统中,当一个对象接收到一条消息时,可能会发生一系列的事件。通常,这些事件是以 同步(synchronous) 模式处理的:调用进程或向这个对象发送消息的线程在发送消息调用完成之前都会接收并处理一系列事件。然而,如果产生这些事件的对象是由多个进程进行共享并且保存在共享内存中时,情况就稍微有些不同了。
本文将使用两种 C++ 设计模式来详细介绍这种情况,并使用一些样例代码来展示这种解决方案(这些样例代码可以从本文 下载 一节中获得):
我们将简要介绍不使用共享内存的样例代码。
使用第一种设计模式来修改这些代码,让其使用共享内存。
然后阐述如何使用第二种设计模式来实现进程间通信(IPC)。 您可以在任何机器体系架构、操作系统和编译器上应用这两种设计模式中的概念。对于本文来说,我们使用的是 RedHat Linux 7.1 for 32-bit x86 Intel? 体系架构的发行版;使用 GNU C++ compiler version 3.2.3 编译器及其相关工具来编译并测试样例程序。
不使用共享内存
下面让我们开始介绍这个样例程序,首先是不使用共享内存的程序:
清单 1. common.h<
#ifndef __COMMON_H__
#define __COMMON_H__
class IObjectWithEvents
{
public:class IEventSink{public:virtual void OnEvent(pid_t pid, const char * msg) = 0;};
static IObjectWithEvents * getInstance();
virtual bool AddEventHandler(IEventSink * pEI) = 0;virtual void SendMessage() = 0;
};
#endif //__COMMON_H__
接口类 IObjectWithEvents 包含了一个内嵌的接口类 IEventSink,它定义了 OnEvent() 方法。这个事件处理程序接收一个发送者的 id 和一个字符串消息。getInstance() 方法返回对共享内存中对象的引用,AddEventHandler() 注册一个事件处理程序,SendMessage() 向这个对象发送一条消息。由于不需要引用共享内存,所以可以像清单 2 中那样来使用 IObjectWithEvents:
清单 2. shm-client1.cpp<
#include
#include
#include
#include "common.h"
#define HERE __FILE__ <<":" <<__LINE__ <<" "
using namespace std;
class EventSink : public IObjectWithEvents::IEventSink
{
public:void OnEvent(pid_t pid, const char * msg){cout <
#include "common.h"
class ObjectWithEvents : public IObjectWithEvents
{
public:// We assume singleton design pattern for illustrationstatic ObjectWithEvents * ms_pObjectWithEvents;
ObjectWithEvents();
//the implementation for IObjectWithEventsvoid FireEvent();virtual bool AddEventHandler(IEventSink * pEI);virtual void SendMessage();
//Collection for maintaining eventsenum { MAX_EVENT_HANDLERS = 16, };long m_npEI;IEventSink * m_apEI[MAX_EVENT_HANDLERS];pid_t m_alPID[MAX_EVENT_HANDLERS];
};
清单 4. ObjectWithEvents.cpp
#include
#include
#include
#include
#include
#include "ObjectWithEvents.h"
using namespace std;
ObjectWithEvents * ObjectWithEvents::ms_pObjectWithEvents = NULL;
IObjectWithEvents * IObjectWithEvents::getInstance()
{// the following commented code is for illustration only./*if (NULL == ObjectWithEvents::ms_pObjectWithEvents){ObjectWithEvents::ms_pObjectWithEvents = new ObjectWithEvents();}*/
return ObjectWithEvents::ms_pObjectWithEvents;
}
ObjectWithEvents::ObjectWithEvents() : m_npEI(0)
{
}
void ObjectWithEvents::FireEvent()
{// iterate through the collectionfor (long i = 0; i
return;
}
bool ObjectWithEvents::AddEventHandler(IEventSink * pEI)
{// NULL checkif (NULL == pEI){return false;}
// check if there is space for this event handlerif (MAX_EVENT_HANDLERS == m_npEI){return false;}
// Add this event handler to the collectionm_alPID[m_npEI] = getpid();m_apEI[m_npEI++] = pEI;
return true;
}
void ObjectWithEvents::SendMessage()
{//Some processing//And then fire the event
FireEvent();
return;
}
清单 4 中的代码可以使用下面的脚本来编译:
g++ -g -o shm_client shm_client1.cpp ObjectWithEvents.cpp
在运行 shm_client 时,应该可以看到下面的输出:
$ ./shm_client shm_client1.cpp:16 Message from pid(3920) :
使用共享内存:没有事件缓存
现在,为了在共享内存中对 ObjectWithEvents 进行实例化,我们需要修改 ObjectWithEvents 的实现。
清单 5. 修改 ObjectWithEvents.cpp// To add a declaration for the "new" operator: class ObjectWithEvents : public IObjectWithEvents { public:void * operator new(unsigned int); }; // To include an additional header for the Initializer class: #include "Initializer.h" // To overload the operator "new": void * ObjectWithEvents::operator new(unsigned int) {return ms_pObjectWithEvents; } // Then, FireEvent is completely changed: void ObjectWithEvents::FireEvent() {// We need to serialize all access to the collection by more than one processint iRetVal = Initializer::LockMutex(); if (0 != iRetVal){return;} pid_t pid = getpid(); // iterate through the collection and fire only events belonging to the current processfor (long i = 0; i < m_npEI; i++){// Check whether the handler belongs to the current process.if (pid != m_alPID[i]){continue;} //Recheck for NULLif (0 != m_apEI[i]){m_apEI[i]->OnEvent(pid, "");}} // release the mutexif ((0 == iRetVal) && (0 != Initializer::UnlockMutex())){// Deal with error.} return; } // The following are changes to ObjectWithEvents::AddEventHandler(): // 1. Before accessing the collection, we lock the mutex: int bRetVal = Initializer::LockMutex(); if (0 != bRetVal) {return false; } // 2. After accessing the collection, we release the mutex: if ((0 == bRetVal) && (0 != Initializer::UnlockMutex())) {// Deal with error. } |
#ifndef __Initializer_H__ #define __Initializer_H__ class Initializer { public : int m_shmid; static Initializer ms_Initializer; Initializer(); static pthread_mutex_t ms_mutex; static int LockMutex(); static int UnlockMutex(); }; #endif // __Initializer_H__ |
#include |
$ ./shm_client shm_client1.cpp:16 Message from pid(4332) : $ ipcs ------ Shared Memory Segments -------- key shmidownerpermsbytesnattch status 0x00001234 327686 sachin 666 136 0 $ ./shm_client shm_client1.cpp:16 Message from pid(4333) : $ ipcrm -m 327686 |
class IObjectWithEvents { public:virtual bool EnqueueEvent(const char * msg) = 0;virtual bool PollForEvents() = 0; }; |
class ObjectWithEvents : public IObjectWithEvents { public:virtual bool EnqueueEvent(const char * msg);virtual bool PollForEvents(); //The event cacheenum { MAX_EVENTS = 16, MAX_EVENT_MSG = 256, };long m_nEvents;pid_t m_alPIDEvents[MAX_EVENTS];char m_aaMsgs[MAX_EVENTS][MAX_EVENT_MSG]; }; |
bool ObjectWithEvents::EnqueueEvent(const char * msg) {if (NULL == msg){return false;} if (MAX_EVENTS == m_nEvents){//IEventSink collection fullreturn false;} int bRetVal = Initializer::LockMutex(); if (0 != bRetVal){return false;} m_alPIDEvents[m_nEvents] = getpid();strncpy(m_aaMsgs[m_nEvents++], msg, MAX_EVENT_MSG - 1); if ((0 == bRetVal) && (0 != Initializer::UnlockMutex())){// Deal with error.} return true; } bool ObjectWithEvents::PollForEvents() {if (0 == m_nEvents){return true;} int bRetVal = Initializer::LockMutex(); if (0 != bRetVal){return false;} pid_t pid = getpid(); for (long i = 0; i < m_npEI; i++){// Does the handler belongs to current process ? if (pid != m_alPID[i]){continue;} //Recheck for NULL if (0 == m_apEI[i]){continue;} for (long j = 0; j < m_nEvents; j++){m_apEI[i]->OnEvent(m_alPIDEvents[j], m_aaMsgs[j]);}} if ((0 == bRetVal) && (0 != Initializer::UnlockMutex())){// Deal with error.} return true; } |
$ ./shm_client1 $ ./ipcs ------ Shared Memory Segments -------- key shmidownerpermsbytesnattch status 0x00001234 360454 sachin 666 4300 0 $ ./shm_client2 shm_client2.cpp:16 Message from pid(4454) : Message from shm_client1 shm_client2.cpp:16 Message from pid(4456) : Message from shm_client2 |
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
去集群 更超群——大容量网络演进之路
2019 IBM 中国论坛
H3C 2019 Navigate 领航者峰会
助推数据中心网络现代化转型 打造灵活可靠基础架构平台