【Linux】linux线程--线程的同步与互斥

本文深入探讨了互斥与同步机制在多线程环境中的应用。解释了互斥锁的工作原理及其如何确保线程安全地访问临界资源,并介绍了条件变量的作用与使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 互斥

1.1 互斥要做的事情

控制线程的访问时序。但多线程能够同时访问到临界资源的时候,有可能会导致线程执行的结果产生二义性。

而互斥就是要保证多个线程在访问同一个临界资源,执行临界区代码的时候(非原子性操作,线程可以被打断)控制访问时序。

让一个线程独占临界资源执行完,再让另一个独占执行。

临界资源:能够被多个线程访问到的资源,称之为临界资源。

临界区代码:访问临界资源的代码。

1.2 如何做到控制线程访问时序(互斥锁)

互斥锁的原理:

互斥锁的本质就是0/1计数器,计数器的取值只能是0或者1;

计数器的值为1的时候:表示当前线程可以获取到互斥锁,而去访问临界资源;

计数器的值为0的时候:表示当前线程不可以获得到互斥锁,从而不能访问临界资源。

加锁:加锁成功的时候,会将计数器的值从1改成0;

解锁:解锁成功的时候,会将计数器的值从0改成1。 

多个线程要执行临界区当中的代码时候为了保证互斥,都是需要先进行加锁保护的。

极端情况下,多个线程同时进行加锁操作,只有一个线程可以枷锁成功,其他线程加锁失败。

1.3 互斥锁的计数器当中如何保证原子性

为什么计数器当中的值从0->1,或者1->0是原子性的呢?

操作不可被分割;要么开始要么没有开始不存在中间状态。

直接使用寄存器当中的值和计数器内存的值交换,而交换时一条汇编指令就可以完成的。

加锁的时候:寄存器当中的值设置为(0),寄存器与计数器当中的值进行交换。

第一种情况:计数器的值为1,说明锁空闲,没有被线程加锁

第二种情况:计数器的值为0,说明锁忙碌,被其他的线程加锁拿走

解锁的时候:寄存器当中的值设置为(1),寄存器与计数器当中的值进行交换。

1.4 互斥锁的接口

1.4.1 初始化  pthread_mutex_t  mutex;

动态初始化:  

int  pthread_mutex_init ( pthread_mutex_t  *restrict  mutex , const  pthread_mutexattr_t  *restrict  arrt ) ;

参数:

     pthread_mutex_t:互斥锁类型(结构体)

     attr:互斥锁的属性,一般直接传递NULL

静态初始化:  pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;

1.4.2 加锁 1->0

int  pthread_mutex_lock ( pthread_mutex_t  *mutex );   --阻塞加锁

int  pthread_mutex_trylock ( pthread_mutex_t  *mutex );   --非阻塞加锁,搭配循环使用判断是否加锁成功

int pthread_mutex_timelock ( pthread_mutex_t  *restrict  mutex , const  struct  timespec  *restrict  abs_timeout );   --带有超时时间的加锁接口

struct  timespec  {

     tim_t  tv_sec; //  secods  5

     long  tv_nsec; //  and  nanoseconds

};

1.4.3 解锁 0->1

int pthread_mutex_unlock ( pthread_mutex_t  *mutex );

线程所有可能退出的地方都进行解锁!

互斥:可以保证多个线程对于临界资源进行访问的互斥属性。

如果只保证互斥,有可能出现“线程饥饿”问题。

线程同步:在保证互斥的前提下,保证多个线程对临界资源访问的合理性。

1.4.4 销毁

int pthread_mutex_distory ( pthread_mutex  *mutex );

1.4.5 使用

代码如下,大意为两个工作线程同时抢票,一共一百张,若没有加入互斥锁,俩线程可能出现二义性,造成线程不安全的情况,而加入互斥锁之后就不存在二义性问题。

    1 #include<stdio.h>
    2 #include<pthread.h>
    3 #include<unistd.h>
    4 int g_tickets=100;
    5 pthread_mutex_t g_lock;
    6 void* mythread_start(void* arg){
    7   while(1){
    8     pthread_mutex_lock(&g_lock);
    9     if(g_tickets<=0){
   10       pthread_mutex_unlock(&g_lock);
   11       break;
   12     }
   13     printf("i am %p,i hace tickets %d\n",pthread_self(),g_tickets);
   14     g_tickets--;
   15     pthread_mutex_unlock(&g_lock);
   16   }
   17   return NULL;
   18 }
   19 int main(){
   20   pthread_mutex_init(&g_lock,NULL);
   21   pthread_t tid[2];
   22   int i;
   23   for(i=0;i<2;i++){
   24     int ret=pthread_create(&tid[i],NULL,mythread_start,NULL);
   25     if(ret<0){
   26         perror("pthread_create\n");
   27         return 0;
   28     }
   29   }
   30   for(i=0;i<2;i++)
   31   {
   32     pthread_join(tid[i],NULL);
   33   }
   34   pthread_mutex_destroy(&g_lock);
   35   return 0;
   36 } 

2 同步

2.1 条件变量

使用原理:线程在加锁之后,判断下临界资源是否可用

     如果可用,则直接访问临界资源

     如果不可用,则调用等待接口,让该线程进行等待

条件变量的原理:

     本质上是PCB等待队列(存放在等待的线程PCB)

2.2 接口

2.2.1 初始化  pthread_cond_signal

动态初始化:

int  pthread_cond_init ( pthread_cond_t  *restrict  cond , const  pthread_condattr_t  *restrict  attr );

参数:

     pthraed_cond_t:条件变量的类型

     pthread_condattr_t:NULL,采用默认属性

int  pthread_cond_destroy ( pthread_cond_t  *cond );

静态初始化:

pthread_cond_t  cond = PTHREAD_COND_INITIALIZER;

2.2.2 等待接口

int pthread_cond_wait ( pthread_cond_t  *restrict  cond , pthread_mudex_t  *restrict  mutex);

作用:谁调用将谁放倒PCB等待队列

参数

     cond:条件变量

     mutex:互斥锁

int  pthread_cond_timedwait ( pthread_cond_t  *retrict  cond , pthread_mutex_t  *restrict  mutex , const  struct  timespec  *restrict  abstime );

2.2.3 唤醒接口

int  pthread_cond_broadcast ( pthread_cond_t  *cond );  //  唤醒所有

int  pthread_cond_signal ( pthread_cond_t  *cond );         //  唤醒至少一个线程

2.3 条件变量的夺命追问

(1)条件变量的扽得改接口第二个参数为什么会有互斥锁?

在函数内部需要解锁操作,如果不解锁就阻塞等待,则其他线程一定拿不到锁。

(2)pthread_cond_wait的内部是针对互斥锁做了什么操作?先释放互斥锁还是先将线程放入到PCB等待队列。

解锁操作;先放入PCB等待队列。

(3)线程被唤醒之后会执行什么代码,需要再获取互斥锁么?

从PCB等待队列当中移出;抢锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值