我是靠谱客的博主 超帅超短裙,最近开发中收集的这篇文章主要介绍关于一个多线程类的设计方法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述


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

进程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的进程,进程里包含各种线程的时候,

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




最后

以上就是超帅超短裙为你收集整理的关于一个多线程类的设计方法的全部内容,希望文章能够帮你解决关于一个多线程类的设计方法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部