关于一个多线程类的设计方法


设计需求描述:如上图所示

进程A与进程B之间需要进程之间通信

进程B与进程C之间需要网络通信


本次设计需求是设计一个进程B,进一步说明如下:

进程B在server A上,可以接收从进程A发过来的进程间消息,并且将消息处理完,通过网络发给server B上的进程C。

server B上的进程C收到消息,经过处理,通过网络发送回复信息给进程B,进程B处理完毕后,发送回进程A。

进程B上收到的消息,处理的过程中需要读取数据库。

经过以上分析,进程B需要完成三个部分的工作,一个是接受请求,一个是接收回复,一个是读取数据库以及监控进程状态。

由于信息的接受和发送时异步的,所以创建三个线程分别取完成各自功能。

分别的功能如下:

线程A  : 接收从进程A发送的请求消息,解包处理,将请求消息通过socket UDP 发送给server B. 转发线程C 请求的hearbeat 消息给 server B。

线程B:接收从server B的进程上发来的UDP 回复消息,解包处理,将回复消息发回给进程A。 将server B回复的heartbeat 消息转发给线程C。

线程C: 连接数据库,读取数据库的信息。 处理线程B转发的heartbeat消息,以确保都是alive的,向线程A发送heartbeat请求,让线程A发送给server B。创建,更新,删除共享内存空间。  关于共享内存,接下来会有单独一篇文章去分析。

heartbeat 如此处理的原因是 因为仅仅只有线程A 能向server B 发送网络消息和线程B能接收server B发送的消息。

依据上述三个功能,分别创建三个功能类,创建对应的头文件.h,以及对应的源文件.cpp.

关于三个类里面如何实现,暂时不说,只是分析说明如果构架这个结构。

功能类A :

class ENUMQuery : public ENUMThread
{
public:
        ENUMQuery();
        ~ENUMQuery();

        /* Entry point of ENUMQuery thread */
        static void     entryPoint(void *);

        /* Cleanup function of ENUMQuery thread */
        static void     cleanup(void *);

        /* Main work of ENUMQuery thread */
        int             run();

        /* Unblock ENUMQuery thread blocked on a condition variable */
        int     condSignal();

        int     processUXMsg(const UXMESSAGE *); /* Could be for query or inter-thread message */

        int     hb_request(const UX_ENUMHBREQ_MSG *);

        int sndCRFQuery(MGENUMREQ_UX *enumReq, int crfindex);

private:
        int portConnect();
        int portDisc();
        int attachLima();
        int detachLima();
        void condTimeout();

private:
        UXipc2          uxipc2;

        static const int timeOutms = 500;

        pthread_cond_t  enumQueryCond;   /* pthread Cond Flag used to suspend ENUMQuery thread */
        pthread_mutex_t enumQueryMutex;  /* pthread mutex used during suspend ENUMQuery thread */

};

功能类B:

class ENUMResponse : public ENUMThread
{
public:
        ENUMResponse();
        ~ENUMResponse();

        /* Entry point of ENUMResponse thread */
        static void     entryPoint(void *);

        /* Cleanup function of ENUMResponse thread */
        static void     cleanup(void *);

        /* Main work of ENUMResponse thread */
        int             run();

        /* Unblock ENUMResponse thread blocked on a condition variable */
        int             condSignal();

        int             RcvDNSMsg(unsigned char *buf, int buf_length, int sockfd, char *remoteip);

        int             ParseResponse( unsigned char *buf, struct sortedEnumRR* enumRsp[], int enumRspNumber, unsigned char& enumRsp_cdnno, unsigned short& enumRsp_ecid );

        void             prepareRR(int RRcounts, struct sortedEnumRR* EnumRRRsp[]);

        int             sndUXMsg(char *orig_called_dgt, unsigned short cdnno, unsigned short ecid, struct sortedEnumRR* enumrr[], int numbers, int rcode);

private:
        int portConnect();
        int portDisc();
private:
        UXipc2          uxipc2;

        pthread_cond_t  enumResponseCond;   /* pthread Cond Flag used to suspend ENUMResponse thread */
        pthread_mutex_t enumResponseMutex;  /* pthread mutex used during suspend ENUMResponse thread */

};

功能类C:

class ENUMOamServer : public ENUMThread
{
// --- public member variable ---
public:

// --- private member variable ---
private:
    // variable for UX communication
    UXipc2 m_uxipc2;

    pthread_cond_t  enumOamServerCond;
    pthread_mutex_t enumOamServerMutex;

        /*
         * Each bit of this variable is used to
         * indicate the status of corresponding
         * crf. 0-oos, 1-active
         */
        UCHAR crfStatMask;

// public member function
public:
    // constructor
    ENUMOamServer();

    // destructor
    virtual ~ENUMOamServer();

    // thread's entry point
    static void entryPoint(void *arg);

    // thread's cleanup function
    static void cleanup(void *arg);

    // unblock the thread
    int condSignal();

    // initialization function
    int init();

    // add entry in routing table
    void addRouter(int crfinex);

    // delete entry in routing table
    void deleteRouter(int crfinex);
private:
    // thread's work function
    int run();

    // handle the database trigger
    void handleDBTrigger(DBDEFAULTTRGMSGTXT *msg);

    // handle HB response from ENUMResponse thread
    void handleHBRsp(UX_ENUMHBRSP_MSG *msg);

    // handle Timer
    void handleTimer(ENUM_TIMER_TAG tag);

    // send HB request to CRF with index
    int sndHBreq(int crfIndex);

    // HB with CRF timeout
    int hbTimeout(int crfindex);

    // dump SHM counts
    void dumpShmInfo();

    // update SHM counts
    void updateShm();

        // Set alarm of communication error with crfindex.
        void setComAlarm(int crfidx);

        //clear alarm of communication error
        void clearComAlarm(int crfidx);
};

有了三个功能类,然后将三个功能需要类放到三个线程里运行,如何需要创建线程?

为了统一管理这三个线程,于是创建了一个线程管理类,使用线程管理类去管理三个功能类,实例化,释放空间,以及获取实例句柄。

这个线程管理类里到底统一对线程做了操作总结:

1. 创建功能类,并未创建线程

2. 释放功能类

3. 获取实例类的句柄


根据需要,在进程B的主函数文件中定义一个全局的线程管理类变量。

有了统一管理接口,可以统一调用实例化功能类,示例如下:

ENUMQuery *queryserver = gENUMThreadManager.createQuery();

ENUMOamServer *oamserver = gENUMThreadManager.createOamServer();

ENUMResponse *responseserver = gENUMThreadManager.createResponse();

那应该什么时候调用管理类去实例化功能类呢?

别着急,我们在三个功能类中都定义了一个静态 入口函数:

        /* Entry point of ENUMQuery thread */
        static void     entryPoint(void *);

    // thread's entry point
    static void entryPoint(void *arg);

    // thread's entry point
    static void entryPoint(void *arg);

在各个功能类的入口函数里,调用线程管理类去完成功能类的实例化。如下所示:

/******************************************************************************
 *  Name:           ENUMQuery::entryPoint
 *
 *  Description:    entry point of ENUMQuery thread.
 *
 *  Inputs:         data - unused (Required by IMplat class)
 *
 *  Outputs:        None
 *
 *  Returns:        None
 *****************************************************************************/
void
ENUMQuery::entryPoint(void *data)
{
        const char *funcName = "ENUMQuery::entryPoint()";
        UX_D3LOG("%s: enter this function!", funcName);

<span style="color:#ff0000;">        ENUMQuery *server = gENUMThreadManager.createQuery();</span>
        if (server != NULL)
        {
                UX_PLOG("%s: The Query thread starts running.", funcName);
                server->run();
        }
        else
        {
                UX_ID_ELOG(EENUM2200, "%s: Create Query instance failed.", funcName);
                return;
        }
        return;
}


一般情况下,我们可以直接在主线程里创建线程,分别执行类的入口函数 ENUMQuery::entryPoint()即可调用管理类实例化线程功能类。

目前看起来好像功能实现了。

但是我们的设计却没有这么做,设计方案里搞出来一个通用平台类,使用这个平台类再去真正创建这几个线程。

先说说这个平台类怎么去创建这三个线程类的:

首先创建一个数组,将要创建的线程都定义在数组中,这个数组类型是一个结构体IMCHILD_THREADS,具体如下所示:

        /************************************************************************
 * Following table contains all threads data for the ENUM process
 * The format of the table is as:
 *  char*               name;           Thread name
 *  int                 idx;            Index
 *  pthread_t           tid;            Thread ID
 *  int                 pidx;           Who is the parent (Index)
 *  PROCCHLD            thrdfn;         Entry function for child thread
 *  PROCCHLDTMR         thrdtmrfn;      Timer function for child thread
 *  PROCCHLDCLN         cleanup;        Cleanup function
 *  int                 lima;           Thread Lima service
 *  int                 rstcnt;         Thread restart count
 *  int                 csiz;           Number of workers
 *  pthread_t*          cctid;          Pointer to worker thread IDs
 *  ThrottleFacility*   throttle;       Pointer to throttle restart object
 *  pid_t               pid;            Process ID
 *  pid_t*              ccpid;          Pointer to worker process ids
 **********************************************************************/

IMplat::IMCHILD_THREADS ENUMthreads[] =
{
     {(char*)"ENUMQuery", ENUMQUERY, 0, IMPLAT_ESS_THRD,
         ENUMQuery::entryPoint, NULL, ENUMQuery::cleanup,
         MGLM_ENUMQUERY, 0, 0, NULL, NULL, -1, NULL},
     {(char*)"ENUMOamServer", ENUMOAMSERVER, 0, IMPLAT_ESS_THRD,
         ENUMOamServer::entryPoint, NULL, ENUMOamServer::cleanup,
         MGLM_ENUMOAMSERVER, 0, 0, NULL, NULL, -1, NULL},
     {(char*)"ENUMResponse", ENUMRESPONSE, 0, IMPLAT_ESS_THRD,
ENUMResponse::entryPoint, NULL, ENUMResponse::cleanup,
         IMNOLIMA, 0, 0, NULL, NULL, -1, NULL}
};

然后在IMplat类的使用数组的变量去创建新的线程出来。

为什么会使用这个平台类呢?这个平台到底做了什么?

如果仅仅是对这三个线程来说,使用这个平台类有些浪费和甚至与特别的拖沓。

但是如果你需要一个很庞大的系统,需要创建有很多个类似于进程B的进程,进程里包含各种线程的时候,

这个平台类的优点就会显示出来,使用通用的平台类,易于管理,会为这些进程事先准备好通用的线程通信接口,和网络通信接口,以及监听进程状态等等。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值