目录
同步互斥的概念
互斥锁
初始化互斥锁
销毁互斥锁
申请上锁
解锁
案例1:没有互斥锁 多任务的运行情况
案例2:有互斥锁 多任务的运行情况
死锁
读写锁
初始化读写锁
销毁读写锁
申请读锁
申请写锁
释放读写锁
案例:两个任务读 一个任务写
条件变量(重要)
概念引入
概念原理
条件变量初始化
释放条件变量
等待条件
唤醒等待在条件变量上的线程
案例:生产者和消费者
信号量
信号量的API
初始化信号量
信号量减一 P操作
信号量加一 V操作
销毁信号量
使用场景
信号量用于线程的互斥
信号量用于线程的同步
无名信号量 用于 血缘关系的进程间互斥
无名信号量 用于 血缘关系的进程间同步
有名信号量 用于 无血缘的进程间互斥
创建一个有名信号量
信号量的关闭
信号量文件的删除
案例:完成互斥
有名信号量 用于 无血缘的进程间同步
同步互斥的概念
互斥:同一时间,只能一个任务(进程或线程)执行,谁先运行不确定。
同步:同一时间,只能一个任务(进程或线程)执行,有顺序的运行。
同步 是特殊的 互斥。
互斥锁
用于线程的互斥。
互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即加锁( lock )和解锁( unlock )
互斥锁的操作流程如下:
1)在访问共享资源临界区域前,对互斥锁进行加锁。
2)在访问完成后释放互斥锁上的锁。 (解锁)
3)对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁 被释放。
互斥锁的数据类型是: pthread_mutex_t
初始化互斥锁
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
功能:
初始化一个互斥锁。
参数:
mutex:互斥锁地址。类型是 pthread_mutex_t 。
attr:设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。
可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁,比如:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init() 来完成动态初始
化,不同之处在于 PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。
返回值:
成功:0,成功申请的锁默认是打开的。
失败:非 0 错误码
销毁互斥锁
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:
销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资 源。
参数:
mutex:互斥锁地址。
返回值:
成功:0
失败:非 0 错误码
申请上锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:
对互斥锁上锁,若互斥锁已经上锁,则调用者阻塞,直到互斥锁解锁后再上锁。
参数:
mutex:互斥锁地址。
返回值:
成功:0
失败:非 0 错误码
int pthread_mutex_trylock(pthread_mutex_t *mutex);
调用该函数时,若互斥锁未加锁,则上锁,返回 0;
若互斥锁已加锁,则函数直接返回失败,即 EBUSY。
解锁
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:
对指定的互斥锁解锁。
参数:
mutex:互斥锁地址。
返回值:
成功:0
失败:非0错误码
案例1:没有互斥锁 多任务的运行情况
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void *deal_fun01(void *arg)
{
char *str = (char*)arg;
while(*str!='\0')
{
printf("%c",*str++);
//没有换行符,需要强制刷新
fflush(stdout);
usleep(500000);
}
return NULL;
}
void *deal_fun02(void *arg)
{
char *str = (char*)arg;
while(*str!='\0')
{
printf("%c",*str++);
//没有换行符,需要强制刷新
fflush(stdout);
usleep(500000);
}
return NULL;
}
int main(int argc, char const *argv[])
{
//创建两个线程
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,deal_fun01,"hello");
pthread_create(&tid2,NULL,deal_fun02,"world");
pthread_detach(tid1);
pthread_detach(tid2);
return 0;
}
由于使用了pthread_detach,创建的线程还没来得及执行,进程就结束了,所以导致输出结果不全
使用pthread_join进行阻塞等待线程结束
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void *deal_fun01(void *arg)
{
char *str = (char*)arg;
while(*str!='\0')
{
printf("%c",*str++);
//没有换行符,需要强制刷新
fflush(stdout);
usleep(500000);
}
return NULL;
}
void *deal_fun02(void *arg)
{
char *str = (char*)arg;
while(*str!='\0')
{
printf("%c",*str++);
//没有换行符,需要强制刷新
fflush(stdout);
usleep(500000);
}
return NULL;
}
int main(int argc, char const *argv[])
{
//创建两个线程
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,deal_fun01,"hello");
pthread_create(&tid2,NULL,deal_fun02,"world");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
案例2:有互斥锁 多任务的运行情况
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
//定义一把锁
pthread_mutex_t mutex;
void *deal_fun01(void *arg)
{
char *str = (char*)arg;
//上锁
pthread_mutex_lock(&mutex);
while(*str!='\0')
{
printf("%c",*str++);
//没有换行符,需要强制刷新
fflush(stdout);
usleep(500000);
}
//解锁
pthread_mutex_unlock(&mutex);
return NULL;
}
void *deal_fun02(void *arg)
{
char *str = (char*)arg;
//上锁
pthread_mutex_lock(&mutex);
while(*str!='\0')
{
printf("%c",*str++);
//没有换行符,需要强制刷新
fflush(stdout);
usleep(500000);
}
//解锁
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(int argc, char const *argv[])
{
//初始化锁
pthread_mutex_init(&mutex,NULL);
//创建两个线程
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,deal_fun01,"hello");
pthread_create(&tid2,NULL,deal_fun02,"world");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
//销毁锁
pthread_mutex_destroy(&mutex);
return 0;
}
由于线程是相互独立的,所以线程处理函数可以合成一个函数
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
//定义一把锁
pthread_mutex_t mutex;
void *deal_fun(void *arg)
{
char *str = (char*)arg;
//上锁
pthread_mutex_lock(&mutex);
while(*str!='\0')
{
printf("%c",*str++);
//没有换行符,需要强制刷新
fflush(stdout);
usleep(500000);
}
//解锁
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(int argc, char const *argv[])
{
//初始化锁
pthread_mutex_init(&mutex,NULL);
//创建两个线程
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,deal_fun,"hello");
pthread_create(&tid2,NULL,deal_fun,"world");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
//销毁锁
pthread_mutex_destroy(&mutex);
return 0;
}
总结:
如果是互斥 不管有多少个任务,只需要一把锁,所有的任务上锁 访问资源 解锁。
死锁
读写锁
POSIX 定义的读写锁的数据类型是: pthread_rwlock_t
初始化读写锁
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock,const pthread_rwlockattr_t *attr);
功能:
用来初始化 rwlock 所指向的读写锁。
参数:
rwlock:指向要初始化的读写锁指针。
attr:读写锁的属性指针。如果 attr 为 NULL 则会使用默认的属性初始化读写锁,否则 使用指定的 attr 初始化读写锁。
可以使用宏 PTHREAD_RWLOCK_INITIALIZER 静态初始化读写锁,比如:
pthread_rwlock_t my_rwlock = PTHREAD_RWLOCK_INITIALIZER;
这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_rwlock_init() 来完成动态初 始化,不同之处在于PTHREAD_RWLOCK_INITIALIZER 宏不进行错误检查。
返回值:
成功:0,读写锁的状态将成为已初始化和已解锁。
失败:非 0 错误码。
销毁读写锁
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
功能:
用于销毁一个读写锁,并释放所有相关联的资源(所谓的所有指的是由 pthread_rwlock_init() 自动申请的资源) 。
参数:
rwlock:读写锁指针。
返回值:
成功:0
失败:非 0 错误码
申请读锁
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
功能:
以阻塞方式在读写锁上获取读锁(读锁定)。
如果没有写者持有该锁,并且没有写者阻塞在该锁上,则调用线程会获取读锁。
如果调用线程未获取读锁,则它将阻塞直到它获取了该锁。一个线程可以在一个读写锁 上多次执行读锁定。
线程可以成功调用 pthread_rwlock_rdlock() 函数 n 次,但是之后该线程必须调用
pthread_rwlock_unlock() 函数 n 次才能解除锁定。
参数:
rwlock:读写锁指针。
返回值:
成功:0
失败:非 0 错误码
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
用于尝试以非阻塞的方式来在读写锁上获取读锁。
如果有任何的写者持有该锁或有写者阻塞在该读写锁上,则立即失败返回
申请写锁
#include <pthread.h>
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
功能:
在读写锁上获取写锁(写锁定)。
如果没有写者持有该锁,并且没有写者读者持有该锁,则调用线程会获取写锁。
如果调用线程未获取写锁,则它将阻塞直到它获取了该锁。
参数:
rwlock:读写锁指针。
返回值:
成功:0
失败:非 0 错误码
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
用于尝试以非阻塞的方式来在读写锁上获取写锁。
如果有任何的读者或写者持有该锁,则立即失败返回。
释放读写锁
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
功能:
无论是读锁或写锁,都可以通过此函数解锁。
参数:
rwlock:读写锁指针。
返回值:
成功:0
失败:非 0 错误码
案例:两个任务读 一个任务写
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//创建一把读写锁
pthread_rwlock_t rwlock;
void *read_data01(void *arg)
{
int *p=(int*)arg;
while(1)
{
//申请上读锁
pthread_rwlock_rdlock(&rwlock);
printf("任务A:num=%d\n",*p);
//解读写锁
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
return NULL;
}
void *read_data02(void *arg)
{
int *p=(int*)arg;
while(1)
{
//申请上读锁
pthread_rwlock_rdlock(&rwlock);
printf("任务B:num=%d\n",*p);
//解读写锁
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
return NULL;
}
void *write_data(void *arg)
{
int *p=(int*)arg;
while(1){
//申请上写锁
pthread_rwlock_wrlock(&rwlock);
(*p)++;
//解读写锁
pthread_rwlock_unlock(&rwlock);
printf("任务C:写入num=%d\n",*p);
sleep(2);
}
return NULL;
}
int main(int argc, char const *argv[])
{
//定义一个公共资源
int num=0;
//初始化读写锁
pthread_rwlock_init(&rwlock,NULL);
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,NULL,read_data01,&num);
pthread_create(&tid2,NULL,read_data02,&num);
pthread_create(&tid3,NULL,write_data,&num);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
//销毁锁
pthread_rwlock_destroy(&rwlock);
return 0;
}
条件变量(重要)
概念引入
对于刚刚死锁的第三种情况。如果任务A先执行上锁,由于管道没有数据,所以读不到数据会一直阻塞,导致无法解锁;任务A无法解锁就会导致任务B无法上锁,也就无法向管道写入数据,同样会导致任务B阻塞等待上锁。
我们按照进程管道通信的方法,可以使用非阻塞读取来解决这个死锁问题。
但我们还可以通过条件变量来进行解决,任务A在执行到读数据的时候,发现管道里没有数据,就会通过条件变量临时解锁,从而通知任务B成功上锁进行数据写入。
概念原理
条件变量是用来等待条件满足而不是用来上锁的,条件变量本身不是锁。
条件变量和互斥锁同时使用。
条件变量的两个动作: 条件不满, 阻塞线程。当条件满足, 通知阻塞的线程开始工作。
条件变量的类型: pthread_cond_t。
原子操作: 几个操作连续进行,不可分割,不能被其它代码操作插入和打断。
条件变量初始化
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond,
const pthread_condattr_t *attr);
功能:
初始化一个条件变量
参数:
cond:指向要初始化的条件变量指针。
attr:条件变量属性,通常为默认值,传NULL即可 。
也可以使用静态初始化的方法,初始化条件变量:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
返回值:
成功:0
失败:非0错误号
释放条件变量
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
功能:
销毁一个条件变量
参数:
cond:指向要初始化的条件变量指针
返回值:
成功:0
失败:非0错误号
等待条件
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond,
pthread_mutex_t *mutex);
功能:
阻塞等待一个条件变量
如果阻塞就 先解锁、等待条件满足、重新上锁(3步为原子操作)解阻塞
参数:
cond:指向要初始化的条件变量指针
mutex:互斥锁
返回值:
成功:0
失败:非0错误号
int pthread_cond_timedwait(pthread_cond_t *cond,
pthread_mutex_t *mutex,const struct *abstime);
功能:
限时等待一个条件变量
参数:
cond:指向要初始化的条件变量指针
mutex:互斥锁
abstime:绝对时间
返回值:
成功:0
失败:非0错误号
唤醒等待在条件变量上的线程
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
功能:
唤醒至少一个阻塞在条件变量上的线程
参数
cond:指向要初始化的条件变量指
返回值
成功:0
失败:非0错误号
int pthread_cond_broadcast(pthread_cond_t *cond);
功能:
唤醒全部阻塞在条件变量上的线程
参数:
cond:指向要初始化的条件变量指针
返回值:
成功:0
失败:非0错误号
案例:生产者和消费者
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
//定义互斥锁
pthread_mutex_t mutex;
//定义条件变量
pthread_cond_t cond;
//定义一个仓库 默认有三个产品
int num = 3;
void *comsumption_fun(void *arg) //消费
{
while (1)
{
//申请上锁
pthread_mutex_lock(&mutex);
//判断仓库是否为空 等待条件变量满足
if (0 == num) //仓库为空
{
printf("%s 发现仓库为空,等待生产\n", (char *)arg);
pthread_cond_wait(&cond, &mutex);
}
else
{
//进入仓库购买产品
num--;
printf("%s 购买了一个产品,仓库剩余%d个\n", (char *)arg, num);
//使用产品
printf("%s 正在使用产品\n", (char *)arg);
}
//解锁
pthread_mutex_unlock(&mutex);
sleep(rand() % 5);
}
}
void *production_fun(void *arg) //生产
{
while (1)
{
//生产一个产品
sleep(rand() % 5);
//上锁 进入仓库
pthread_mutex_lock(&mutex);
//将产品放入仓库
num++;
printf("%s 放入一个产品,仓库剩余%d\n", (char *)arg, num);
//通知 条件变量阻塞的线程
pthread_cond_broadcast(&cond);
//解锁
pthread_mutex_unlock(&mutex);
}
}
int main(int argc, char const *argv[])
{
//设计随机数种子
srand(time(NULL));
//初始化锁
pthread_mutex_init(&mutex, NULL);
//初始化条件变量
pthread_cond_init(&cond, NULL);
pthread_t tid1, tid2, tid3;
pthread_create(&tid1, NULL, comsumption_fun, "消费者A");
pthread_create(&tid2, NULL, comsumption_fun, "消费者B");
pthread_create(&tid3, NULL, production_fun, "生产者");
//等待线程结束
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
//销毁锁
pthread_mutex_destroy(&mutex);
//销毁条件变量
pthread_cond_destroy(&cond);
return 0;
}
信号量
信号量也叫信号灯,其本质就是一个计数器,描述临界资源的数目大小。(最多能有多少资源分配给线程)。
信号量可参考:信号量的概念 及其 操作函数(有名、无名信号量)_仲夏夜之梦~的博客-CSDN博客
前面的锁和条件变量只用于线程间同步互斥。而信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被 用来控制对公共资源的访问。
适用于六种情况:线程间的同步与互斥、有血缘进程间的同步与互斥、无血缘进程间的同步与互斥。
当信号量值大于 0 时,则可以访问,否则将阻塞。
PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。
号量数据类型为:sem_t
信号量用于互斥:
不管多少个任务互斥 只需要一个信号量(与前面的锁机制相同)。先P 任务 再 V
信号量用于同步:
有多少个任务 就需要多少个信号量。最先执行的任务对应的信号量为1,其他信号量全 部为0。
每任务先P自己 任务 再V下一个任务的信号量
信号量的API
初始化信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value)
功能:
创建一个信号量并初始化它的值。一个无名信号量在被使用前必须先初始化。
参数:
sem:信号量的地址
pshared:等于 0,信号量在线程间共享(常用);不等于0,信号量在进程间共享。
value:信号量的初始值
返回值:
成功:0
失败: - 1
信号量减一 P操作
int sem_wait(sem_t *sem);
功能: 将信号量减一,如果信号量的值为0 则阻塞,大于0可以减一
参数:信号量的地址
返回值:成功返回0 失败返回-1
尝试对信号量减一
int sem_trywait(sem_t *sem);
功能: 尝试将信号量减一,如果信号量的值为0 不阻塞,立即返回 ,大于0可以减一
参数:信号量的地址
返回值:成功返回0 失败返回-1
信号量加一 V操作
int sem_post(sem_t *sem);
功能:将信号量加一
参数:信号量的地址
返回值:成功返回0 失败返回-1
销毁信号量
int sem_destroy(sem_t *sem);
功能: 销毁信号量
参数: 信号量的地址
返回值:成功返回0 失败返回-1
使用场景
信号量用于线程的互斥
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>
//定义一个信号量(用于互斥)
sem_t sem;
void my_print(char* str)
{
int i=0;
while(str[i]!='\0')
{
printf("%c",str[i++]);
fflush(stdout);
sleep(1);
}
return;
}
void *task_fun(void *arg)
{
//P操作
sem_wait(&sem);
my_print((char*)arg);
//V操作
sem_post(&sem);
return NULL;
}
int main(int argc, char const *argv[])
{
//信号量初始化为1
sem_init(&sem,0,1);
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,NULL,task_fun,"hello");
pthread_create(&tid2,NULL,task_fun,"world");
pthread_create(&tid3,NULL,task_fun,"nanjing");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
//销毁信号量
sem_destroy(&sem);
return 0;
}
信号量用于线程的同步
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>
//定义一个信号量(用于互斥)
sem_t sem1,sem2,sem3;
void my_print(char* str)
{
int i=0;
while(str[i]!='\0')
{
printf("%c",str[i++]);
fflush(stdout);
sleep(1);
}
return;
}
void *task_fun01(void *arg)
{
//P操作
sem_wait(&sem2);
my_print((char*)arg);
//V操作
sem_post(&sem3);
return NULL;
}
void *task_fun02(void *arg)
{
//P操作
sem_wait(&sem3);
my_print((char*)arg);
//V操作
sem_post(&sem1);
return NULL;
}
void *task_fun03(void *arg)
{
//P操作
sem_wait(&sem1);
my_print((char*)arg);
//V操作
sem_post(&sem2);
return NULL;
}
int main(int argc, char const *argv[])
{
//信号量初始化为1
sem_init(&sem1,0,1);
sem_init(&sem2,0,0);
sem_init(&sem3,0,0);
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,NULL,task_fun01,"hello");
pthread_create(&tid2,NULL,task_fun02,"world");
pthread_create(&tid3,NULL,task_fun03,"nanjing");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
//销毁信号量
sem_destroy(&sem1);
sem_destroy(&sem2);
sem_destroy(&sem3);
return 0;
}
无名信号量 用于 血缘关系的进程间互斥
如下图所示,通过一个普通的信号量想要实现父子进程间的互斥,由于父子进程使用的是独立的代码空间,因此当fork后,两个进程都会执行PV操作,因此这种方法无法实现进程互斥。
我们可以保证信号量是父子进程公共识别的,让信号量脱离父子进程空间。我们可以通过磁盘映射、存储映射、内存共享
#include <sys/mman.h>
#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>
#include <sys/wait.h>
void my_print(char *str)
{
int i = 0;
while (str[i] != '\0')
{
printf("%c", str[i++]);
fflush(stdout);
sleep(1);
}
return;
}
int main(int argc, char const *argv[])
{
//定义一个无名信号量
// MAP_ANONYMOUS匿名映射(不使用文件描述符),fd为-1表示不需要文件描述符
sem_t *sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
//无名信号量的初始化 参数2:1表示进程
sem_init(sem, 1, 1);
pid_t pid = fork();
if (pid == 0) //子进程
{
// P操作
sem_wait(sem);
my_print("hello");
// V操作
sem_post(sem);
}
else if (pid > 0) //父进程
{
// P操作
sem_wait(sem);
my_print("world");
// V操作
sem_post(sem);
int status = 0;
pid_t pid = wait(&status);
}
//销毁信号量
sem_destroy(sem);
}
无名信号量 用于 血缘关系的进程间同步
进程间的同步需要用到两个信号量
#include <sys/mman.h>
#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>
#include <sys/wait.h>
void my_print(char *str)
{
int i = 0;
while (str[i] != '\0')
{
printf("%c", str[i++]);
fflush(stdout);
sleep(1);
}
return;
}
int main(int argc, char const *argv[])
{
//定义一个无名信号量
// MAP_ANONYMOUS匿名映射(不使用文件描述符),fd为-1表示不需要文件描述符
sem_t *sem1 = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
sem_t *sem2 = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
//无名信号量的初始化 参数2:1表示进程
sem_init(sem1, 1, 1);
sem_init(sem2, 1, 0);
pid_t pid = fork();
if (pid == 0) //子进程
{
// P操作
sem_wait(sem1);
my_print("hello");
// V操作
sem_post(sem2);
}
else if (pid > 0) //父进程
{
// P操作
sem_wait(sem2);
my_print("world");
// V操作
sem_post(sem1);
int status = 0;
pid_t pid = wait(&status);
}
//销毁信号量
sem_destroy(sem1);
sem_destroy(sem2);
}
有名信号量 用于 无血缘的进程间互斥
有名信号量的使用类似于文件操作,有名信号量创建完成之后,当前整个系统有效,直到系统重启或通过sem_unlink函数手动删除。
创建一个有名信号量
创建并初始化有名信号量或打开一个已存在的有名信号量。
注: 如果指定了O_CREAT(而没有指定O_EXCL),那么只有所需的信号量尚未存在时才初始化他。 如果所需的信号量已经存在,mode,value都会被忽略。
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
//信号量存在
sem_t *sem_open(const char *name, int oflag);
//信号量不存在
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
功能:
创建一个信号量
参数:
name:信号量的名字
oflag:sem_open函数的权限标志
mode:文件权限(可读、可写、可执行 0777)的设置
value:信号量的初始值
返回值:
信号量的地址,失败:SEM_FAILED
信号量的关闭
int sem_close(sem_t *sem);
功能:关闭信号量
参数:信号量的的地址
返回值:成功0 失败-1
信号量文件的删除
#include <semaphore.h>
int sem_unlink(const char *name);
功能:删除信号量的文件
参数:信号量的文件名
返回值:成功0 失败-1
案例:完成互斥
TaskA.c
#include <stdio.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
void my_print(char *str)
{
int i = 0;
while (str[i] != '\0')
{
printf("%c", str[i++]);
fflush(stdout);
sleep(1);
}
return;
}
int main(int argc, char const *argv[])
{
//创建一个有名信号量sem_open类似文件操作 最后一个参数为初始值
sem_t *sem = sem_open("sem",O_RDWR|O_CREAT,0666,1);
//P 操作
sem_wait(sem);
//任务
my_print("hello world");
//V 操作
sem_post(sem);
//关闭信号量
sem_close(sem);
//销毁信号量
sem_destroy(sem);
return 0;
}
TaskB.c
#include <stdio.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
void my_print(char *str)
{
int i = 0;
while (str[i] != '\0')
{
printf("%c", str[i++]);
fflush(stdout);
sleep(1);
}
return;
}
int main(int argc, char const *argv[])
{
//创建一个有名信号量sem_open类似文件操作 最后一个参数为初始值
sem_t *sem = sem_open("sem",O_RDWR|O_CREAT,0666,1);
//P 操作
sem_wait(sem);
//任务
my_print("nanjing 8.20");
//V 操作
sem_post(sem);
//关闭信号量
sem_close(sem);
//销毁信号量
sem_destroy(sem);
return 0;
}
运行效果:先运行A,再运行B。A关闭有名信号量前,B无法打开有名信号量(阻塞)
有名信号量 用于 无血缘的进程间同步
TaskA.c
#include <stdio.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
void my_print(char *str)
{
int i = 0;
while (str[i] != '\0')
{
printf("%c", str[i++]);
fflush(stdout);
sleep(1);
}
return;
}
int main(int argc, char const *argv[])
{
//创建一个有名信号量sem_open类似文件操作 最后一个参数为初始值
sem_t *sem1 = sem_open("sem1",O_RDWR|O_CREAT,0666,1);
sem_t *sem2 = sem_open("sem2",O_RDWR|O_CREAT,0666,0);
//P 操作
sem_wait(sem1);
//任务
my_print("hello world");
//V 操作
sem_post(sem2);
//关闭信号量
sem_close(sem1);
sem_close(sem2);
//销毁信号量
sem_destroy(sem1);
sem_destroy(sem2);
return 0;
}
TaskB.c
#include <stdio.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
void my_print(char *str)
{
int i = 0;
while (str[i] != '\0')
{
printf("%c", str[i++]);
fflush(stdout);
sleep(1);
}
return;
}
int main(int argc, char const *argv[])
{
//创建一个有名信号量sem_open类似文件操作 最后一个参数为初始值
sem_t *sem1 = sem_open("sem1",O_RDWR|O_CREAT,0666,1);
sem_t *sem2 = sem_open("sem2",O_RDWR|O_CREAT,0666,0);
//P 操作
sem_wait(sem2);
//任务
my_print("nanjing 8.20");
//V 操作
sem_post(sem1);
//关闭信号量
sem_close(sem1);
sem_close(sem2);
//销毁信号量
sem_destroy(sem1);
sem_destroy(sem2);
return 0;
}