在代码的逻辑中,我们经常需要把一些操作放到一个线程中去执行,比如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 无法释放。