looper线程的简单实现

本文探讨了在代码逻辑中使用Looper线程的重要性,特别是在Android中确保UI更新在主线程进行。通过一个C语言实现的Looper线程示例,解释了为何在处理消息时不需要加锁,因为消息一旦取出并处理,不会被其他线程修改。同时,文章提到了潜在的死锁问题,当Looper线程与MQTT客户端操作交互时,如果两者锁的获取顺序不一致,可能导致死锁。最后,提供了一个简单的Looper API使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在代码的逻辑中,我们经常需要把一些操作放到一个线程中去执行,比如android 中的更新UI就只能在main线程中执行,这样做是为了避免有些资源被两个(多个)线程“同时”修改。下面是用c语言实现的looper线程。


#ifndef __MESSAGE_LOOPER_H__
#define __MESSAGE_LOOPER_H__

#include <pthread.h>

#define MAX_MESSAGE_SIZE  256
#define MESSAGE_EXIT_LOOP  -1

typedef struct message_t
{
    int what;
    struct message_t* next;
    int data_size;
    void* data;
} message_t;

typedef struct{
   message_t* head;
   int size;
} message_queue_t;

typedef void (*CALLBACK_FUNC)(message_t *msg);

typedef struct{
    int is_loop;  //退出线程循环标志
    pthread_t looper_thread;
    pthread_mutex_t queue_mutex;
    pthread_cond_t queue_cond;
    message_queue_t queue;
    CALLBACK_FUNC handle_msg; //消息处理函数
} message_looper_t;


int create_looper(message_looper_t** looper,CALLBACK_FUNC func);

int start_loop(message_looper_t* looper);

int stop_loop(message_looper_t* looper);

int destroy_looper(message_looper_t* looper);

int push_message(message_looper_t* looper,int what,const void* data,int size);

#endif

对应的实现代码


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "message_looper.h"

static message_t* fetch_message(message_queue_t* queue)
{
    if(queue->size<1)
    {
        return NULL;
    }
    message_t* message = queue->head;
    queue->head=message->next;    //取出队列头的消息后,消息队列指向下一个
    (queue->size)--;
    return message;
}

static int delete_message(message_t* message)
{
    if(message==NULL)
    {
        printf("[message_looper] delete_message message is null.\n");
        return -1;
    }
    if(message->data!=NULL)
    {
         free(message->data);
         message->data = NULL;
    }
    free(message);
    return 0;
}

static int clear_message_queue(message_queue_t* queue)
{
    if(queue==NULL)
    {
        return -1;
    }
    message_t* message;
    while((queue->size)>0)
    {
        message = queue->head;
        queue->head=message->next;    //取出队列头的消息后,消息队列指向下一个
        (queue->size)--;
        delete_message(message);
    }
    return 0;
}

static void* message_loop(void *arg)
{
    message_looper_t* looper = (message_looper_t*)arg;

    while(looper->is_loop)
    {
        pthread_mutex_lock(&(looper->queue_mutex));        //获取锁
        if((looper->queue).size<1)
        {
            pthread_cond_wait(&(looper->queue_cond),&(looper->queue_mutex));  //等待这个信号量
            pthread_mutex_unlock(&(looper->queue_mutex));  //下次进入判断队列是否有消息
            continue;
        }
        message_t* msg= fetch_message(&(looper->queue));
        if(msg == NULL)
        {
            pthread_mutex_unlock(&(looper->queue_mutex));  //释放锁
            continue;
        }
        pthread_mutex_unlock(&(looper->queue_mutex));      //释放锁

        /*
         * 如果handle_msg() 可能会导致死锁,比如异步消息需要push到这个线程中
         * 刚好这个线程正在处理消息,然后出现消息又需要用到异步发送消息的函数
         * 异步发送消息的函数中又刚好需要锁,这时候会出现死锁
         */
        looper->handle_msg(msg);  //处理消息
        delete_message(msg);    //删除消息

    }
    clear_message_queue(&(looper->queue));  //最后清空链表
    return NULL;
}

////////////////////////////////////////////////////////////////////////

int create_looper(message_looper_t** looper,CALLBACK_FUNC func)
{
    if(looper == NULL || func == NULL)
    {
          printf("[message_looper] create_looper looper or func is null.\n");
          return -1;
    }

    *looper = (message_looper_t*)malloc(sizeof(message_looper_t));
    if(*looper == NULL)
    {
        printf("[message_looper] create_looper malloc fail.\n");
        return -1;
    }
    (*looper)->is_loop = 0;
    pthread_mutex_init(&((*looper)->queue_mutex),NULL);
    pthread_cond_init(&((*looper)->queue_cond),NULL);
    (*looper)->queue.head = NULL;
    (*looper)->queue.size = 0;
    (*looper)->handle_msg = func;
    return 0;
}

int start_loop(message_looper_t* looper)
{
    if(looper == NULL)
    {
        printf("[message_looper] start_loop looper is null.\n");
        return -1;
    }

    pthread_mutex_lock(&(looper->queue_mutex));   //加锁保护
    if(looper->is_loop)
    {
        printf("[message_loop] start_loop message_loop had start.\n");
        pthread_mutex_unlock(&(looper->queue_mutex));   //释放锁
        return -1;
    }
    looper->is_loop = 1;   //标志创建了 looper线程
    if(pthread_create(&(looper->looper_thread), NULL, message_loop, looper))
    {
        printf("[message_looper] pthread_create message_loop fail.\n");
        looper->is_loop = 0;  //线程创建失败,loop标志为false
        pthread_mutex_unlock(&(looper->queue_mutex));   //释放锁
        return -1;
    }

    pthread_mutex_unlock(&(looper->queue_mutex));   //释放锁
    return 0;
}

int stop_loop(message_looper_t* looper)
{
    if(looper == NULL)
    {
        printf("[message_looper] stop_loop loop is null.\n");
        return -1;
    }
    pthread_mutex_lock(&(looper->queue_mutex));   //加锁保护
    looper->is_loop = 0; //标志线程结束
    pthread_mutex_unlock(&(looper->queue_mutex));   //释放锁
    push_message(looper,MESSAGE_EXIT_LOOP,NULL,0);  //线程可能还在等待消息,push 一个消息,让它结束运行
    pthread_join(looper->looper_thread, NULL);   //等待线程运行结束
    return 0;
}

int destroy_looper(message_looper_t* looper)
{
    if(looper == NULL)
    {
        printf("[message_looper] destroy_loop looper is null.\n");
        return -1;
    }

    if(looper->is_loop)
    {
        printf("[message_looper] destroy_loop looper is start.\n");
        stop_loop(looper); //结束线程
    }

    pthread_cond_destroy(&(looper->queue_cond));
    pthread_mutex_destroy(&(looper->queue_mutex));
    free(looper);

    return 0;
}

int push_message(message_looper_t* looper,int what,const void* data,int size)
{
    if(looper == NULL)
    {
        printf("[message_looper] push_message looper is null.\n");
        return -1;
    }
    pthread_mutex_lock(&(looper->queue_mutex));
    if((looper->queue).size>MAX_MESSAGE_SIZE)  //当前队列中的消息太多了,还没来得及处理
    {
        printf("[message_looper] queue.size(%d) more than %d.\n",(looper->queue).size,MAX_MESSAGE_SIZE);
        pthread_mutex_unlock(&(looper->queue_mutex));
        return -1;
    }
    message_t* message=(message_t*)malloc(sizeof(message_t));
    message->what=what;
    message->next = NULL;

    if(data!=NULL&&size>0)  //copy data
    {
       message->data=malloc(size);
       memcpy(message->data,data,size);
       message->data_size=size;
    }else{
       message->data = NULL;
       message->data_size = 0;
    }

    if((looper->queue).head == NULL)  //头部为空时,直接指向新的消息
    {
        (looper->queue).head = message;
        ((looper->queue).size)++;
        pthread_cond_signal(&(looper->queue_cond));
        pthread_mutex_unlock(&(looper->queue_mutex));
        return 0;
    }
    message_t* tmp = (looper->queue).head;
    while(tmp->next!=NULL)  //新的消息发到链表尾部
    {
        tmp = tmp->next;
    }
    tmp->next = message;
    ((looper->queue).size)++;
    pthread_cond_signal(&(looper->queue_cond));
    pthread_mutex_unlock(&(looper->queue_mutex));
    return 0;
}

调用handle_msg()  处理队列中的消息时为什么没有加锁保护?

 /*
         * 如果handle_msg() 可能会导致死锁,比如异步消息需要push到这个线程中
         * 刚好这个线程正在处理消息,然后出现消息又需要用到异步发送消息的函数
         * 异步发送消息的函数中又刚好需要锁,这时候会出现死锁
         */
        looper->handle_msg(msg);  //处理消息
        delete_message(msg);    //删除消息

如上面的代码,为什么没有加锁保护的范围里面,一个是没有必要,因为message 已经从消息队列里面取出来了,并且处理这个消息只会在这个线程里面完成,不会有其他地方会修改到它; 第二个放在加锁保护的范围里面反而可能导致死锁。

之前在使用MQTT 的时候遇到过死锁的情况
int message_arrived(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
       push_message(...);   //提交到looper线程中处理
}

在looper 线程的handle_msg() 函数调用到了 MQTTClient_publishMessage(...) ;

原因就是message_arrived ()拿了MQTT的锁,需要looper的锁,而handle_msg()拿了looper的锁,需要MQTT的锁。

 

-----------------------------------------------------------------------------------------------------------------------------------------------------

一个简单使用looper API的demo


#include <stdio.h>
#include <string.h>
#include "message_looper.h"

static void handle_message(message_t *msg)
{
    printf("handle_message msg = %d \n",msg->what);
}

int main()
{
    message_looper_t *looper;

    if(create_looper(&looper,handle_message))
    {
        printf("create_looper fail.\n");
        return -1;
    }

    if(start_loop(looper))
    {
        printf("start_loop fail.\n");
        destroy_looper(looper);
        return -1;
    }

    int cmd;
    while (1)
    {
        cmd = getchar();
        while (getchar() != 10);  //一直读到回车才结束循环
        if(cmd == '0')
            break;
        push_message(looper,cmd,NULL,0);  //将消息发送到looper线程中执行
    }

    destroy_looper(looper);
    return 0;
}
stop_loop(message_looper_t* looper) 的时候可能还有些问题,如果looper线程先执行了
clear_message_queue(&(looper->queue));  //最后清空链表
然后调用stop_loop的线程再执行
push_message(looper,MESSAGE_EXIT_LOOP,NULL,0);  //线程可能还在等待消息,push 一个消息,让它结束运行
就会导致这个message 无法释放。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值