线程学习(二)

本文详细解析了线程安全的重要性,介绍了互斥锁的实现原理及其在多线程环境中的应用,包括死锁的产生与避免策略,以及条件变量如何增强线程间的有序交互。

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

线程安全:

  • 为什么有线程安全,而进程没有?                                                                                                                                                    

       因为进程是独立的,所以进程间不会相互影响,而一个线程可能会影响其他线程。多个线程因为临界资源的争抢写入操作会导致程序逻辑的混乱和数据二义性,因此就引入了线程安全的概念。

          线程互斥:

实现:互斥锁 ---- 实际是一个计数器但只有0/1.。它是为保证数据同一时间唯一访问性

0 ---- 表示没有资源,不能操作(阻塞/报错返回)

1 ---- 表示有资源,能操作

 特性:

1.  互斥锁不仅保证别的临界资源,自己也是临界资源

2.  为保证自己安全,所以互斥锁是原子性操作

3它将多线程并行操作改为串行操作,所以它会降低性能。

  • 为什么互斥锁没有时序性? 

因为:没有条件判断; 没有通知等待在条件变量上的线程。

互斥量实现原理:

  •  i++,++i 都不是原子的,它有可能被切出去,有可能会有数据一致性问题;
  • 为实现互斥锁操作,操作系统提供了swap 和 exchange指令, / / exchange保证mutex只有一个锁

作用:将寄存器和内存单元的数据交换它。 只有一条指令,所以是原子性。

 

       互斥锁实现:

创建互斥锁

    pthread_mutex_t lock;

初始化互斥锁

    pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);   // 动态分配
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  // 静态分配 

加锁

         计数位1,则置零,继续操作;计数位为0则阻塞/报错返回。

    pthread_mutex_lock(pthread_mutex_t *mutex); // 阻塞加锁            
    pthread_mutex_trylock(pthread_mutex_t *mutex); // 非阻塞加锁 
    pthread_mutex_timedlock(pthread_mutex_t *_restict_mutex); // 限时阻塞加锁

解锁:解锁操作 ---> 计数器置1.

    #include <pthread.h> 
    int pthread_mutex_unlock(pthread_mutex_t *mutex);

死锁:一个程序一直获取不到锁,因此一直处于卡死状态就称为死锁。

产生死锁的四个必要条件:

1. 互斥条件 : 一个资源每次只能被一个执行流执行; 

2. 不可剥夺条件 : 一个执行流拿到一个锁,只能由该执行流去释放锁;

3. 请求与保持条件: 一个执行流拿到一个锁,再去请求第二个锁时,不释放第一个;

4. 环路等待条件 : 两个执行流相互等待对方的锁。

避免死锁:

1. 破坏死锁的必要条件;

2. 加锁顺序一致;

3. 避免锁未释放的场景;

4. 资源一次性分配 。      

                    销毁互斥锁:

    #include <pthread.h> 
    int pthread_mutex_destroy(pthread_mutex_t *mutex);

                    /*

                       销毁互斥量需要注意:

                            1. 使⽤ "PTHREAD_ MUTEX_ INITIALIZER" 初始化的互斥量不需要销毁;

                            2. 不要销毁⼀个已经加锁的互斥量;

                            3. 已经销毁的互斥量,要确保后⾯不会有线程再尝试加锁。

                    */

  • 互斥量mutex:

pthread_mutex_t 

pthread_mutex_t  lock

创建互斥锁

pthread_mutex_init 

pthread_mutex_init(pthread_mutex_t *restrict mutex,

const pthread_mutexattr_t *restrict attr);

 

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

初始化

pthread_mutex_lock 

pthread_mutex_lock(pthread_mutex_t *mutex);

阻塞加锁

pthread_mutex_trylock 

pthread_mutex_trylock(pthread_mutex_t *mutex);

非阻塞加锁

pthread_mutex_timedlock 

pthread_mutex_timedlock(pthread_mutex_t *_restict_mutex);

限时阻塞加锁

pthread_mutex_unlock 

pthread_mutex_unlock(pthread_mutex_t *mutex);

解锁

pthread_mutex_destroy 

pthread_mutex_destroy(pthread_mutex_t *mutex);

销毁

 

 

线程同步:

实现:条件变量 --- 让数据的访问更加具有时序的可控性

  • cond:条件变量

pthread_cond_t cond 

pthread_cond_t cond  cond

创建条件变量

 

pthread_cond_init

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

 

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

初始化变量条件 

 

pthread_cond_destroy

pthread_cond_destroy(pthread_cond_t *cond);

销毁

 

pthread_cond_wait

pthread_cond_wait(pthread_cond_t *restrict cond,

pthread_mutex_t *restrict mutex);

死等

死等之前需要解锁,并且解锁和死等是原子操作,被唤醒后需要加锁。但这个锁是非阻塞的,如果有多个线程时,就需要循环判断。

pthread_cond_timedwait

pthread_cond_wait (pthread_cond_t *restrict cond, pthread_mutex_ *restrict mutex);

限时等待

 

pthread_cond_signal

pthread_cond_signal(pthread_cond_t *cond);

单个唤醒,在等待队列的线程

 

pthread_cond_broadcast

pthread_cond_broadcast(pthread_cond_t *cond);

广播

 

              通常在多对多情况下,为了防止出现唤醒错误,需要两个条件变量。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值