一、信号量
信号量如同一盏红绿信号灯,用于临界资源(如公路、人行道)的管理。信号量是一种特殊的变量,访问具有原子性。
P等待:信号量的值为0时,不能减,则进行阻塞休眠。相当于绿灯变红灯,不可以走。
V信号:信号量加1,则信号量的值变大,如果为1,则可以唤醒其他阻塞的线程。相当于有红灯变带有秒数的绿灯。
二、信号量相关函数
#include<semaphore.h>
//信号量变量声明
sem_t sem;
//初始化一个信号量,可以设置一个默认值value,[0,max unsigned int)
int sem_init(sem_t *sem,int pshared,unsigned int value);
//销毁信号量
int sem_destroy(sem_t * sem)
//原子操作方式信号量减1,信号量为0则阻塞不能减1。阻塞休眠
int sem_wait(sem_t *sem)
//原子操作方式信号量减1,信号量为0则不能减1。不阻塞直接返回
int sem_trywait(sem_t *sem)
//信号量加1,可以唤醒正在等待的该信号量的任何被阻塞的线程
int sem_post(sem_t *sem)
//获取信号量sem的当前值,把该值保存sval中
int sem_getvalue(sem_t *sem,int *sval);
三、二值信号量
相当于一盏红绿灯,只有红或绿两种状态(绿灯是没有带秒数的)。
1、作为锁使用
作锁使用,信号量初始值为1,但不同线程的临界区不同。
2、追踪线程。
追踪线程,信号量初始值为0,如主线启动子线程后,进入sem_wait()进入阻塞,等待另一线程sem_post().然后主线才能继续执行。
四、使用案列
ndk-samples中工程native-codec中有一looper类,进行跨线程通信。
#include "looper.h"
#include <assert.h>
#include <jni.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <semaphore.h>
// for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");
#include <android/log.h>
#define TAG "NativeCodec-looper"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
struct loopermessage;
typedef struct loopermessage loopermessage;
struct loopermessage {
int what;
void *obj;
loopermessage *next;
bool quit;
};
void* looper::trampoline(void* p) {//执行线程循环
((looper*)p)->loop();
return NULL;
}
looper::looper() {
sem_init(&headdataavailable, 0, 0);//头信息可用
sem_init(&headwriteprotect, 0, 1);//头写入保护
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&worker, &attr, trampoline, this);//创建线程
running = true;
}
looper::~looper() {
if (running) {
LOGV("Looper deleted while still running. Some messages will not be processed");
quit();//终止线程
}
}
void looper::post(int what, void *data, bool flush) {//外部线程添加消息事件
loopermessage *msg = new loopermessage();
msg->what = what;
msg->obj = data;
msg->next = NULL;
msg->quit = false;
addmsg(msg, flush);
}
void looper::addmsg(loopermessage *msg, bool flush) {
sem_wait(&headwriteprotect);//写入保护信号量>=1可减1继续执行。写入保护信号量为0,则先等待该信号量其他地方加1后,方可减1执行。
loopermessage *h = head;
if (flush) {//清空
while(h) {
loopermessage *next = h->next;
delete h;
h = next;
}
h = NULL;
}
if (h) {//追加到链表尾部
while (h->next) {
h = h->next;
}
h->next = msg;
} else {
head = msg;//作为头部
}
LOGV("post msg %d", msg->what);
sem_post(&headwriteprotect);
sem_post(&headdataavailable);//数据可用信号量标记是否用信息可用
}
void looper::loop() {
while(true) {
// wait for available message
sem_wait(&headdataavailable);//数据可用信号量先判断否用有信息可用
// get next available message
sem_wait(&headwriteprotect);//写保护信号量如果非0,先减1,继续执行。如果0则进入等待。
loopermessage *msg = head;
if (msg == NULL) {
LOGV("no msg");
sem_post(&headwriteprotect);
continue;
}
head = msg->next;
sem_post(&headwriteprotect);//取一个消息后,写保护信号量加1。
if (msg->quit) {
LOGV("quitting");
delete msg;
return;
}
LOGV("processing msg %d", msg->what);
handle(msg->what, msg->obj);//处理消息
delete msg;
}
}
void looper::quit() {
LOGV("quit");
loopermessage *msg = new loopermessage();
msg->what = 0;
msg->obj = NULL;
msg->next = NULL;
msg->quit = true;
addmsg(msg, false);
void *retval;
pthread_join(worker, &retval);//等待自线程结束
sem_destroy(&headdataavailable);//销毁信号量
sem_destroy(&headwriteprotect);
running = false;
}
void looper::handle(int what, void* obj) {
LOGV("dropping msg %d %p", what, obj);
}