线程同步——互斥锁
目录
一、基本概念
二、打印成对出现的字母
三、生产者消费者(有限缓冲问题)
3.1 基本概念
3.2 代码实现
一、基本概念
互斥锁是一种用于控制对共享资源访问的同步机制。它确保在同一时间内,只有一个线程可以访问被保护的资源。互斥锁有两个基本操作:
-
加锁(Lock):当一个线程需要访问共享资源时,它会尝试获取互斥锁。如果锁是可用的(即未被其他线程持有),则该线程成功获取锁并进入临界区(Critical Section),执行对共享资源的操作。如果锁已经被其他线程持有,则该线程会被阻塞,直到锁被释放。
-
解锁(Unlock):当线程完成对共享资源的操作后,它会释放互斥锁,允许其他等待的线程获取锁并访问共享资源。
二、打印成对出现的字母
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>//定义了一个全局互斥锁mutex,用于同步线程对共享资源的访问。
pthread_mutex_t mutex;//锁的使用在funa,funb 中
void* funa(void* arg)
{for(int i=0;i<5;i++){printf("A");fflush(stdout);int n=rand()%3;sleep(n);printf("A");//第二次打印代表对打印机的使用结束fflush(stdout);n=rand()%3;sleep(n);}
}void* funb(void* arg)
{for(int i=0;i<5;i++){printf("B");fflush(stdout);int n=rand()%3;sleep(n);printf("B");//第二次打印代表对打印机的使用结束fflush(stdout);n=rand()%3;sleep(n);}
}int main()
{//初始化全局互斥锁mutex,第二个参数为NULL表示使用默认属性。pthread_mutex_init(&mutex,NULL);//锁的地址,锁的属性//启动俩个线程pthread_t id1,id2;pthread_create(&id1,NULL,funa,NULL);pthread_create(&id2,NULL,funb,NULL);//等待线程结束pthread_join(id1,NULL);pthread_join(id2,NULL);//销毁,锁变量的地址pthread_mutex_destroy(&mutex);
}
这个输出结果表明程序中存在线程同步问题,导致输出结果并不是预期的AABB
定义了一个全局互斥锁
mutex
,用于同步线程对共享资源的访问。
输出预期值AABBAABB 成对出现
需要加锁
prhread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex);
三、生产者消费者(有限缓冲问题)
3.1 基本概念
俩个或更多的线程共享同一个缓冲区,其中一个或多个线程作为“生产者”会不断的向缓冲区中添加数据,另一个或多个线程作为消费者从缓冲区中取走数据。
生产者和消费者必须互斥的使用缓冲区
缓冲区空时,消费者不能读取数据
缓冲区满时,生产者不能添加数据
s1 代表空闲空间有多少个 s2代表数据有多少个
如果在ps1之前加,那么所有人都不能操作缓冲区了 。先看有空间可不可以写入,有空间再加锁,否则加锁后操作不了缓冲区了。只要数据写入之后就立刻解锁释放
3.2 代码实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>#define BUF_SIZE 30
pthread_mutex_t mutex;
sem_t sem_full;//信号量
sem_t sem_empty;int in=0;//在那个数据可以写入数据
int out=0;//在那个可以读取
int buff[BUF_SIZE];//缓冲区
void* sc_fun(void* arg)
{for(int i=0;i<30;i++){sem_wait(&sem_empty);pthread_mutex_lock(&mutex);buff[in]=rand()%100;printf("生产者在%d位置产生数据:%d\n",in,buff[in]);in=(in+1)%BUF_SIZE;pthread_mutex_unlock(&mutex);sem_post(&sem_full);int n=rand()%3;sleep(n);}
}void* xf_fun(void* arg)
{for(int i=0;i<20;i++){sem_wait(&sem_full);pthread_mutex_lock(&mutex);printf("----------消费者在%d位置消费数据:%d\n",out,buff[out]);out=(out+1)%BUF_SIZE;pthread_mutex_unlock(&mutex);sem_post(&sem_empty);//让空闲格子加1int n=rand()%3;sleep(n);}
}int main()
{pthread_mutex_init(&mutex,NULL);sem_init(&sem_full,0,0);//信号量初始化sem_init(&sem_empty,0,BUF_SIZE);//为空的有多少个pthread_t sc_id[2];pthread_t xf_id[3];for(int i=0;i<2;i++){pthread_create(&sc_id[i],NULL,sc_fun,NULL);}for(int i=0;i<3;i++){pthread_create(&xf_id[i],NULL,xf_fun,NULL);}for(int i=0;i<2;i++){pthread_join(sc_id[i],NULL);}for(int i=0;i<3;i++){pthread_join(xf_id[i],NULL);}pthread_mutex_destroy(&mutex);sem_destroy(&sem_full);sem_destroy(&sem_empty);exit(0);
}
sem_full
和sem_empty
是信号量,分别表示缓冲区中已占用的位置数和空闲位置数。
in
和out
分别表示下一个可写和可读的位置。
buff
是缓冲区数组,用于存储数据。输出中可以看到生产者和消费者线程交替进行操作,这表明它们在并发环境中工作。
生产者在生产数据前会等待一个空闲位置(
sem_wait(&sem_empty)
),然后锁定互斥锁,生产数据,解锁互斥锁,并释放一个已占用位置(sem_post(&sem_full)
)。消费者在消费数据前会等待一个已占用位置(
sem_wait(&sem_full)
),然后锁定互斥锁,消费数据,解锁互斥锁,并释放一个空闲位置(sem_post(&sem_empty)
)。已占用:表示该位置已经被生产者放入了数据,并且这些数据尚未被消费者取出。缓冲区中已经被生产者放入数据的位置。缓冲区可以被看作是一个固定大小的数组,用于临时存储生产者生成的数据,直到消费者将其取出。