在Linux环境中用模拟Windows中的线程消息队列

      本文对于内核中加入了System V 进程通信机制的系统来说是多余的,因为System V 进程通信机制中已经实现了消息队列。对于那些未将System V 进程通信选入内核编译的系统(本文中将要描述的就是这类情况,下面所提到的都是这种Linux系统,不再特别说明),要如何实现类似Windows的线程消息队列操作(PostThreadMessage / GetMessage)呢?

      众所周知,Windows的线程消息队列是很方便的一种机制,用它来做线程间的通信、同步不失为一种很不错的途径(本人就很喜欢用这种方法)。那么在Linux系统中又将如何来实现类似的线程通信机制呢?本人的做法是使用UNIX域协议(UNIX Domain)的Socket来模拟,虽然功能达不到Windows的线程消息队列那样强大,但对于一般的线程通信操作已经够用了。

      为什么要用UNIX Domain Socket 而不是TCP/UDP,是因为前者对于位于同一主机的通信效率是最高的(它没有协议栈的处理,速度大概是后者的两倍)。本文不介绍UNIX Domain Socket 以及Linux线程的使用,下面介绍上述模拟过程的实现:

 

      首先,每个线程都要建立一个消息队列,在Windows中这个工作是系统自动完成的,我们现在需要模拟,那么就要维护一张整个进程中所有线程的消息队列的全局表,可以用链表来实现之:

 

线程消息队列类型:

typedef struct msg_queue
{
    pthread_t tid;
    int sock_fd;
    char quename[32];
    struct msg_queue *next_que;
} msg_queue_t, *pmsg_queue_t;

 

线程消息结构:

typedef struct msg_head
{
    ushort msg;
    ushort len;
    uchar msg_buf[256];
}msg_head_t, *pmsg_head_t;

全局链表:

static pmsg_queue_t lpmsg_queue = NULL;

全局锁,用来临界全局线程消息队列表的操作,防止冲突:
static pthread_mutex_t msg_que_mutex = PTHREAD_MUTEX_INITIALIZER;

 

从队列中接收消息的操作是阻塞的,windows中让线程结束消息循环就是发送WM_QUIT消息到目标线程,因此我们也模拟一个这样的消息:

#define QUIT_MESSAGE 0

 

若要向指定线程发送消息,首先需要寻找到它的消息队列:

static pmsg_queue_t find_msg_queue(pthread_t tid)
{
    pthread_mutex_lock(&msg_que_mutex);
    pmsg_queue_t ptemp = lpmsg_queue;
    while (ptemp)
    {
        if (ptemp->tid == tid)
          break;

        ptemp = ptemp->next_que;
    }

    pthread_mutex_unlock(&msg_que_mutex);
    return ptemp;
}

 

往全局线程消息队列表中加入新的线程消息队列:

static pmsg_queue_t add_new_queue(pthread_t tid)
{
    pmsg_queue_t ptemp = NULL, ptail = NULL;
    struct sockaddr_un addr;

    pthread_mutex_lock(&msg_que_mutex);
    if (NULL == lpmsg_queue)
    {
        lpmsg_queue = (pmsg_queue_t)calloc(1, sizeof(msg_queue_t));
        ptemp = lpmsg_queue;
    }
    else
    {
        ptemp = lpmsg_queue;
        while (ptemp->next_que)
        {
            ptemp = ptemp->next_que;
        }

        ptail = ptemp;
        ptemp = (pmsg_queue_t)calloc(1, sizeof(msg_queue_t));
        ptail->next_que = ptemp;
    }
   
    if (ptemp)
    {
        ptemp->tid = tid;
        if ((ptemp->sock_fd = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0)
        {
            free(ptemp);
            ptemp = NULL;
        }
        else
        {
            sprintf(ptemp->quename, "/tmp/que_%d", (int)tid);  //用线程ID来做名称,确保唯一性
            unlink(ptemp->quename);   //防止重名的发生,先做删除操作
       
            memset(&addr, 0, sizeof(struct sockaddr_un));
            addr.sun_family = PF_UNIX;
            strcpy(addr.sun_path, ptemp->quename);
       
            if (bind(ptemp->sock_fd, (struct sockaddr *)&addr, sizeof(addr)))
            {
                close(ptemp->sock_fd);
                free(ptemp);
                ptemp = NULL;
            }
        }
    }

    pthread_mutex_unlock(&msg_que_mutex);
    return ptemp;
}


若线程结束,则要从全局线程消息队列表中删除其线程消息队列:

void delete_msg_que()
{
    pmsg_queue_t ptemp = NULL, ptail = NULL;
    pmsg_queue_t premove = NULL;

    premove = find_msg_queue(pthread_self());   
    if (NULL == premove) return;

    pthread_mutex_lock(&msg_que_mutex);
   
    ptemp = lpmsg_queue;
    while (ptemp)
    {
        ptail = ptemp;   
        if (ptemp == premove)
            break;
   
        ptemp = ptemp->next_que;
    }

    if (ptemp)
    {
        if (ptemp == lpmsg_queue)
            lpmsg_queue = lpmsg_queue->next_que;
        else ptail->next_que = ptemp->next_que;

        close(ptemp->sock_fd);
        unlink(ptemp->quename);
        free(ptemp);
    }

    pthread_mutex_unlock(&msg_que_mutex);
}

 

为当前线程创建线程消息队列:

pthread_t create_msg_que()
{
    pthread_t tid = pthread_self();

    if (NULL == find_msg_queue(tid))
        if (NULL == add_new_queue(tid))
            return 0;

    return tid;
}

 

      至此,所有的准备工作都已经就绪,可以实现最有意义的功能了,那就是接收、发送线程消息。

 

从消息队列中接收线程消息:

uchar get_message(pmsg_head_t msg_hd)
{
    int n = 0, addlen = 0;
    pmsg_queue_t ptemp = NULL;
    struct sockaddr_un addr;
   
    if (NULL != (ptemp = find_msg_queue(pthread_self())))
    {
        memset(&addr, 0, sizeof(struct sockaddr_un));
        addlen = sizeof(addr);
        n = recvfrom(ptemp->sock_fd, msg_hd, sizeof(msg_head_t), 0, (struct sockaddr *)&addr, &addlen);

        if (n > 0)
            return (QUIT_MESSAGE != msg_hd->msg);
    }

    return 0;
}

 

发送线程消息到指定的线程消息队列:

int post_message(const pthread_t thread_id,
         const ushort msg,
         const ushort msg_len,
         const uchar *msg_data)
{
    struct sockaddr_un addr;
    msg_head_t msg_hd;
    pmsg_queue_t pthis = NULL, ptemp = find_msg_queue(thread_id);
   
    if (NULL == ptemp) return 0;

    pthread_t tid = pthread_self();
    if (NULL == (pthis = find_msg_queue(tid)))
    pthis = add_new_queue(tid);

    if (pthis)
    {
        memset(&addr, 0, sizeof(struct sockaddr_un));
        addr.sun_family = PF_UNIX;
        strcpy(addr.sun_path, ptemp->quename);

        msg_hd.msg = msg;
        if (msg_len > 0 && NULL != msg_data)
        {
            msg_hd.len = msg_len;
            memcpy(msg_hd.msg_buf, msg_data, msg_len);
        }

        return sendto(pthis->sock_fd, &msg_hd, 4 + msg_len, 0, (struct sockaddr *)&addr, sizeof(addr));
    }

    return 0;
}

 

      以上就是本人模拟实现Linux环境中的线程消息队列机制的全过程,请大家多多批评指正,要拍砖的话还望手下留情!

阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鲫鱼跃龙门

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

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

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

打赏作者

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

抵扣说明:

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

余额充值