信号量是一种用于提供不同进程或一个给定进程的不同线程间同步手段。
在Posix中,已经有一套信号接口,用于同一个进程中不同线程同步,其接口为:
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_destroy(sem_t *sem);
int sem_getvalue(sem_t *sem);
但Posix标准实现信号量的主要的目的是提供一种进程间同步的方式,注意重点是解决进程间,不过信号量也可以用于线程间同步。
进程间同步通过有名信号量(sem_open(), sem_close(), sem_unlink(), sem_wait(), sem_post(), sem_getvalue(), sem_trywait())
线程间同步一般通过互斥锁和条件变量来实现,但具体情况,还要看应用场景。
下面主要介绍通过互斥锁和条件变量实现信号实现,代码比较简单:
#ifndef __DL_SEMA_H__
#define __DL_SEMA_H__
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
typedef void* dl_sema_t;
extern int
dl_sema_create(dl_sema_t *pSema, unsigned int init_cnt);
extern int
dl_sema_destroy(dl_sema_t sema);
extern int
dl_sema_p(dl_sema_t sema, int timeout);
extern int
dl_sema_v(dl_sema_t sema);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif /* __DL_SEMA_H__ */
#include <pthread.h>
#include <stdlib.h>
#include <sys/time.h> /* gettimeofday() */
#include "dl_sema.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
#define SEMA_MAGIC 0xF147258F
struct sema_info{
unsigned int magic;
int cnt;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
/* abstime = millsec + the current time */
void dl_gettime(struct timespec *abstime, int millsec)
{
long sec, nsec;
struct timeval cur_time;
gettimeofday(&cur_time, 0);
nsec = cur_time.tv_usec * 1000;
sec = cur_time.tv_sec;
nsec += (millsec%1000)*1000000;
abstime->tv_nsec = nsec % 1000000000;
sec += millsec/1000;
sec += nsec / 1000000000;
abstime->tv_sec = sec;
}
/* create a semaphore with init_cnt */
int dl_sema_create(dl_sema_t *pSema, unsigned int init_cnt)
{
struct sema_info *sema;
if(!pSema) return -1;
sema = malloc(sizeof(struct sema_info));
if(!sema) return -1;
if(pthread_mutex_init(&(sema->mutex), 0) != 0){
free(sema);
return -1;
}
if( pthread_cond_init(&(sema->cond), 0) != 0){
pthread_mutex_destroy(&(sema->mutex));
free(sema);
return -1;
}
sema->cnt = init_cnt;
sema->magic = SEMA_MAGIC;
*pSema = sema;
return 0;
}
int dl_sema_p(dl_sema_t sema, int timeout)
{
struct sema_info *psema = (struct sema_info*) sema;
int ret = 0;
if(!psema || (psema->magic != SEMA_MAGIC) ) return -1;
pthread_mutex_lock(&(psema->mutex));
if(psema->cnt <= 0){
/* wait forever */
if(timeout == 0){
while(psema->cnt <= 0){
if((ret = pthread_cond_wait(&(psema->cond), &(psema->mutex))) != 0){
pthread_mutex_unlock(&(psema->mutex));
return ret;
}
}
}
else {
struct timespec abs_time;
dl_gettime(&abs_time, timeout);
while(psema->cnt <= 0){
ret = pthread_cond_timedwait(&(psema->cond),
&(psema->mutex),
&abs_time);
if(ret != 0){
pthread_mutex_unlock(&(psema->mutex));
return ret;
}
}
}
}
psema->cnt--;
pthread_mutex_unlock(&(psema->mutex));
return 0;
}
int dl_sema_v(dl_sema_t sema)
{
int ret = 0;
struct sema_info* psema = (struct sema_info*)sema;
if(!psema || (psema->magic != SEMA_MAGIC) ) return -1;
pthread_mutex_lock(&(psema->mutex));
psema->cnt++;
ret = pthread_cond_signal(&(psema->cond));
if(ret != 0){
psema->cnt--; /* roll back */
pthread_mutex_unlock(&(psema->mutex));
return ret;
}
pthread_mutex_unlock(&(psema->mutex));
return 0;
}
int dl_sema_destroy(dl_sema_t sema)
{
struct sema_info* psema = (struct sema_info*)sema;
int ret = 0;
if(!psema || (psema->magic != SEMA_MAGIC) ) return -1;
psema->magic = 0;
if((ret = pthread_mutex_destroy(&(psema->mutex))) != 0) return ret;
if((ret = pthread_cond_destroy(&(psema->cond))) != 0) return ret;
free(psema);
return 0;
}
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
//简单测试代码:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include "dl_sema.h"
void *thread_1(void *arg)
{
int ret;
dl_sema_t sema = (dl_sema_t)arg;
ret = dl_sema_p(sema, 3000);/* 3s timeout */
if(ret == 0)printf("the semaphore come up !\n");
dl_sema_destroy(sema);
return 0;
}
void *thread_2(void *arg)
{
dl_sema_t sema = (dl_sema_t)arg;
dl_sema_v(sema);
printf("OK!!!!\n");
return 0;
}
int main(int argc, char**argv)
{
pthread_t thid;
dl_sema_t sema;
if(dl_sema_create(&sema, 0))
printf("dl_sema_init failed.\n");
pthread_create(&thid, 0, thread_1, sema);
pthread_create(&thid, 0, thread_2, sema);
sleep(5);
return 0;
}