目录
一、信号量
1.函数
2.使用
二、读写锁
1.函数
2.使用
三、互斥锁
1.函数
2.使用
四、条件变量
1.函数
2.使用
前言
线程同步的实现方法:信号量、互斥锁、条件变量、读写锁。
下面就对着四种方法进行展开描述
一、信号量
与进程间通信的信号量类似,参考链接:【Linux】进程间通信——信号量
1.函数
头文件
#include<semaphore.h>
sem_init()
int sem_init(sem_t *sem,int pshared,unsigned int value);
- 作用:初始化由sem指向的信号量对象
- 参数:pshared控制信号量类型,如果为0表示这个信号量是当前进程的局部信号量,否则,这个信号量就可以在多个进程之间共享。
- 返回值:成功返回0
sem_post()
int sem_post(sem_t *sem);
- 作用:原子操作的方式给信号量的值+1。所谓原子操作是指,如果两个线程企图同时给一个信号量加1,他们之间不会互相干扰,而不像如果两个程序同时对同一个文件进行读取、增加、写入操作时可能会引起冲突。信号量的值总是被正确地加2,因为有两个线程企图改变它。
- 参数:指针作为参数,指向对象是由sem_init调用的初始化信号量。
- 返回值:成功返回0
sem_wait()
int sem_wait(sem_t *sem);
- 作用:原子操作的方式给信号量的值-1。但他会等待直到信号量有个非零值才会开始减法操作。因此,如果对值为2的信号量调用sem_wait,线程将继续执行,但信号量的值会减到1.如果对值为0的信号量进行操作,sem_wait函数就会进行等待,直到有其他线程增加了该信号量的值使其不再是0为止。如果两个线程同时在sem_wait调用上等待同一个信号量变为非0值,那么该信号量被第三个线程增加1时,只有其中一个等待线程将开始对信号量-1,然后继续执行,另一个线程还将继续等待。
- 参数:指针作为参数,指向对象是由sem_init调用的初始化信号量
- 返回值:成功返回0
sem_destroy()
int sem_destroy(sem_t *sem);
- 作用:用完信号量之后对它进行清理。
- 指针作为参数,指向对象是由sem_init调用的初始化信号量,并清理该信号量拥有的所有资源。
- 返回值:如果企图清理的信号量正在被一些线程等待,就会收到一个错误。成功返回0。
2.使用
用代码实现打印机对ABC顺序打印的操作,如下图所示,使用信号量完成代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
sem_t sema;
sem_t semb;
sem_t semc;
void* funa(void* arg)
{
for(int i=0;i<5;i++)
{
sem_wait(&sema);//ps1
printf("A");
fflush(stdout);
sem_post(&semb);//vs2
}
}
void* funb(void* arg)
{
for(int i=0;i<5;i++)
{
sem_wait(&semb);
printf("B");
fflush(stdout);
sem_post(&semc);
}
}
void* func(void* arg)
{
for(int i=0;i<5;i++)
{
sem_wait(&semc);
printf("C");
fflush(stdout);
sem_post(&sema);
}
}
int main()
{
//初始化信号量
sem_init(&sema,0,1);
sem_init(&semb,0,0);
sem_init(&semc,0,0);
//创建线程
pthread_t id1,id2,id3;
pthread_create(&id1,NULL,funa,NULL);
pthread_create(&id2,NULL,funb,NULL);
pthread_create(&id3,NULL,func,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
sem_destroy(&sema);
sem_destroy(&semb);
sem_destroy(&semc);
exit(0);
}
二、读写锁
由上面的程序可知,信号量的使用是A在读取时,阻止B,C的读取,这样拉低程序性能,读取的时候其实可以三个一起读,而在写操作的时候不能读,因此引入读写锁进行优化
1.函数
pthread_rwlock_init();//初始化
pthread_rwlock_destroy();//销毁
pthread_rwlock_rdlock();//读
pthread_rwlock_wrloc();//读写
pthread_rwlock_unlock();//解锁
要求:
- 在写时,不能读
- 在读时,不能读
- 读的话都可以读
2.使用
代码:
读锁阻止写锁
写锁阻止读锁和写锁
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
pthread_rwlock_t lock;
void* fun1(void* arg)
{
for(int i=0;i<5;i++)
{
pthread_rwlock_rdlock(&lock);
printf("fun1 read start----\n");
sleep(1);
printf("fun1 over\n");
pthread_rwlock_unlock(&lock);
sleep(1);
}
}
void* fun2(void* arg)
{
for(int i=0;i<5;i++)
{
pthread_rwlock_rdlock(&lock);
printf("fun2 read start----\n");
sleep(2);
printf("fun2 over\n");
pthread_rwlock_unlock(&lock);
}
}
void* fun3(void* arg)//写
{
for(int i=0;i<5;i++)
{
pthread_rwlock_wrlock(&lock);
printf("fun3 read start----\n");
sleep(3);
printf("fun3 over\n");
pthread_rwlock_unlock(&lock);
}
}
int main()
{
pthread_rwlock_init(&lock,NULL);
pthread_t id1,id2,id3;
pthread_create(&id1,NULL,fun1,NULL);
pthread_create(&id2,NULL,fun2,NULL);
pthread_create(&id3,NULL,fun3,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
pthread_rwlock_destroy(&lock);
exit(0);
}
三、互斥锁
用的时候加锁,如果已经被别的进程加锁,那么该进程就只能被阻塞着。
互斥锁可以理解为信号量的子集
1.函数
头文件
#include<pthread.h>
返回值:成功返回0,失败返回错误码
参数:与信号量类似,都是一个提前声明过的对象指针。对互斥锁来说,这个对象的类型为pthread_mutex_t。
pthread_mutex_init——初始化
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr);
用于设置互斥量的属性,控制互斥量的行为
pthread_mutex_lock——加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_unlock——解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread_mutex_destroy——销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
2.使用
用代码实现互斥锁的使用,在打印机内A使用前加锁,使用后解锁,B在A使用的时候阻塞,如下图·所示:
代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
pthread_mutex_t mutex;
void* fun1(void* arg)
{
for(int i=0;i<5;i++)
{
pthread_mutex_lock(&mutex);
printf("A");
fflush(stdout);
int n=rand()%3;
sleep(n);
printf("A");
fflush(stdout);
pthread_mutex_unlock(&mutex);
n=rand()%3;
sleep(n);
}
}
void* fun2(void* arg)
{
for(int i=0;i<5;i++)
{
pthread_mutex_lock(&mutex);
printf("B");
fflush(stdout);
int n=rand()%3;
sleep(n);
printf("B");
fflush(stdout);
pthread_mutex_unlock(&mutex);
n=rand()%3;
sleep(n);
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);//初始化
pthread_t id1,id2;
pthread_create(&id1,NULL,fun1,NULL);
pthread_create(&id2,NULL,fun2,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_mutex_destroy(&mutex);//销毁
exit(0);
}
运行结果:
四、条件变量
条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待 这个共享数据的线程。
唤醒时,如果线程不在等待队列中的(条件变量为空),此时唤醒就无意义
1.函数
头文件
#include<pthread.h>
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_signal(pthread_cond_t *cond); //唤醒单个线程
int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒所有等待的线程
int pthread_cond_destroy(pthread_cond_t *cond);
2.使用
下面代码举例说明funa使用时加锁,用完解锁。funb使用时加锁,用完解锁。键盘输入数据唤醒funa,funb其中一个进行读取。如果键盘输入为end,funa,funb同时被唤醒,退出进程。
加锁的目的防止被他人唤醒被他人加入等待队列
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
pthread_mutex_t mutex;
pthread_cond_t cond;
void* funa(void* arg)
{
char*s=(char*)arg;//退化为指向数组首地址指针
while(1)//先阻塞,直到被唤醒
{
pthread_mutex_lock(&mutex);//加锁 用互斥锁保护
pthread_cond_wait(&cond,&mutex);//加锁,解锁
pthread_mutex_unlock(&mutex);//解锁
if(strncmp(s,"end",3)==0)
{
break;
}
printf("funa: %s",s);
}
}
void* funb(void* arg)
{
char*s=(char*)arg;//退化为指向数组首地址指针
while(1)//先阻塞,直到被唤醒
{
pthread_mutex_lock(&mutex);//加锁 用互斥锁保护
pthread_cond_wait(&cond,&mutex);//加锁,解锁
pthread_mutex_unlock(&mutex);//解锁
if(strncmp(s,"end",3)==0)
{
break;
}
printf("funb: %s",s);
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
char buff[128]={0};
pthread_t id1,id2;
pthread_create(&id1,NULL,funa,(void*)buff);
pthread_create(&id2,NULL,funb,(void*)buff);
while(1)
{
fgets(buff,128,stdin);
if(strncmp(buff,"end",3)==0)
{
//唤醒所有线程
pthread_cond_broadcast(&cond);
break;
}
else
{
//唤醒一个线程
pthread_cond_signal(&cond);
}
}
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}