文章目录
互斥和同步概述
在多任务操作系统中,同时运行的多个任务可能 都需要访问/使用同一种资源,多个任务之间有依赖关系,某个任务的运行依赖于另一个任务,同步和互斥就是用于解决这两个问题的。
互斥:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。POSIX 标准中 进程和线程同步和互斥的方法,主要有信号量和互斥锁两种方式。
同步:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行
互斥锁
互斥锁的概念
mutex 是一种通过加锁的方式来控制对共享资源的访问,如全局变量,mutex只有上锁和解锁,在访问该资源时,要先申请mutex,如果时解锁的状态,则执行,如果是上锁的状态,则默认阻塞这个申请者
初始化互斥锁
mutex 用 pthread_mutex_t 数据类型表示,在使用互斥锁前,必须先对它进行初始化。
静态分配的互斥锁:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
动态分配互斥锁:
pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL);
在所有使用过此互斥锁的线程都不再需要使用时候,应调用 销毁互斥锁 pthread_mutex_destroy 销毁互斥锁。
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr); 功能:初始化一个互斥锁。
参数:mutex:互斥锁地址。 attr:互斥锁的属性,NULL 为默认的属性。
返回值:成功返回 0,失败返回非
互斥锁上锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:对互斥锁上锁,若已经上锁,则调用者一直阻塞到互斥锁解锁。
参数:mutex:互斥锁地址。
返回值:成功返回 0,失败返回非 0。
互斥锁上锁 2
#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能:对互斥锁上锁,若已经上锁,则上锁失败,函数立即返回。
参数:
mutex:互斥锁地址。
返回值:成功返回 0,失败返回非 0。
互斥锁解锁
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t * mutex);
功能:对指定的互斥锁解锁。
参数:mutex:互斥锁地址。
返回值:成功返回 0,失败返回非 0。
销毁互斥锁
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:销毁指定的一个互斥锁。 参数:mutex:互斥锁地址。
返回值:成功返回 0,失败返回非 0。
互斥锁的代码演示
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int j = 0;
pthread_mutex_t mutex;
void *callback(void *arg)
{
pthread_mutex_lock(&mutex);//上锁
for (int i = 0; i < 3;i++)
{
printf("j=%d\n",j);
j++;
sleep(1);
}
pthread_mutex_unlock(&mutex);//解锁
}
void *callback1(void *arg)
{
pthread_mutex_lock(&mutex);//上锁
for (int i = 0; i < 3;i++)
{
printf("j=%d\n",j);
j--;
sleep(1);
}
pthread_mutex_unlock(&mutex);//解锁
}
void *(*str[2])(void *) ={ callback,callback1};
int main()
{
pthread_mutex_init(&mutex, NULL);
pthread_t pth[1000];
int i = 0;
for (i = 0; i < 2;i++)
{
pthread_create(&pth[i], NULL, str[i], NULL);
}
pthread_join(pth[0], NULL);
pthread_join(pth[1], NULL);
pthread_mutex_destroy(&mutex); //用完之后要销毁互斥锁
}
注意:编译的时候要加上 -lpthread链接
信号量
信号量的概念
信号量本质上是一个非负的整数计数器,它被用来控制对公共 资源的访问。 编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于0时,则可以访问, 否则将阻塞。 PV原语是对信号量的操作,一次P操作使信号量 sem 减1,一次V操作使信号量 sem 加1。
信号量主要用于进程或线程间的同步和互斥这两种典型情况。
1、若用于互斥,几个进程(或线程)往往只设置一个信号量。
2、若用于同步操作,往往会设置多个信号量,并且安排不同的初始值,来实现它们之间的执行顺序。 信号量用于互斥
信号量的初始化
信号量的初始化
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared,unsigned int value);
功能:创建一个信号量并初始化它的值。
参数:
sem:信号量的地址。
pshared:等于 0,信号量在线程间共享;不等于 0,信号量在进程间共享。
value:信号量的初始值。
返回值:成功返回 0,失败返回-1。
信号量 P 操作(减)
#include <semaphore.h> int sem_wait(sem_t *sem);
功能:将信号量的值减 1,若信号量的值小于 0,此函数会引起调用者阻塞。
参数:
sem:信号量地址。
返回值:成功返回 0,失败返回-1。
#include <semaphore.h>
int sem_trywait(sem_t *sem);
功能:将信号量的值减 1,若信号量的值小于 0,则对信号量的操作失败,函数立即返回。
参数:
sem:信号量地址。
返回值:成功返回 0,失败返回-1
信号量的 V 操作(加)
#include <semaphore.h>
int sem_post(sem_t *sem);
功能:将信号量的值加 1 并发出信号唤醒等待线程。
参数:
sem:信号量地址。
返回值:成功返回 0,失败返回-1。
获取信号量的计数值
#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);
功能:获取 sem 标识的信号量的值,保存在 sval 中。
参数:
sem:信号量地址。
sval:保存信号量值的地址。
返回值:成功返回 0,失败返回-1
信号量的销毁
#include <semaphore.h>
int sem_destroy(sem_t *sem);
功能:删除 sem 标识的信号量。
参数:sem:信号量地址。
返回值:成功返回 0,失败返回-1。
信号量代码演示
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
pthread_t pid,pid1;
int a = 0;
sem_t sem;
//信号量的初始化
void *thread(void *arg)
{
sem_wait(&sem);
for (int i = 0; i < 3;i++)
{
a++;
printf("%d\n" ,a);
sleep(1);
}
sem_post(&sem);
}
void *thread1(void *arg)
{
for (int i = 0; i < 3;i++)
{
sem_wait(&sem);
printf("%d\n", a);
a--;
sem_post(&sem);
sleep(1);
}
}
int main()
{
sem_init(&sem, 0,1);
pthread_create(&pid, NULL,thread, NULL);
pthread_create(&pid1, NULL,thread1, NULL);
sleep(2);
pthread_join(pid, NULL);//阻塞,等待子线程退出
pthread_join(pid1, NULL);//阻塞,等待子线程退出
}
有名信号量(扩展)
有名信号量概念
其实 POSIX 的信号量有两种:
1、无名信号量
2、有名信号量
前面我们介绍的就是无名信号量,无名信号量一般用于线程间同步或互斥。 而有名信号量一般用于进程间同步或互斥。
有名信号量的打开或创建
#include <fcntl.h>
#include <sys/stat.h>
#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:信号量文件名。
flags:sem_open 函数的行为标志。
mode:文件权限(可读、可写、可执行)的设置。
value :信号量初始值。 返回值:成功返回信号量的地址,失败返回 SEM_FAILED。
信号量的关闭
#include <semaphore.h>
int sem_close(sem_t *sem);
功能:关闭有名信号量。
参数:
sem:指向信号量的指针。
返回值:成功返回 0,失败返回-1。
信号量的删除
#include <semaphore.h>
int sem_unlink(const char *name);
功能:删除信号量的文件。
参数:
name:信号量文件名。
返回值:成功返回 0,失败返回-1。
有名信号量代码演示
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include <semaphore.h>
void my_printer(char *str)
{
while(*str != '\0'){
putchar(*str);
fflush(stdout);
str++;
sleep(1);
}
printf("\n");
}
int main(int argc ,char *argv[])
{
//打开有名信号量
sem_t *sem=sem_open("my_sem", O_CREAT,0666, 1);
sem_t *sem1=sem_open("my_sem1", O_CREAT,0666, 0);
int i=0;
for(i=0;i<3;i++){
//对其--
sem_wait(sem);
my_printer("11111111111111");
sem_post(sem1);
}
//关闭有名信号量
sem_close(sem);
sem_close(sem1);
//删除有名信号量
sem_unlink(sem);
sem_unlink(sem1);
return 0;
}