【Linux】线程安全——同步和互斥

需要云服务器等云产品来学习Linux的同学可以移步/–>腾讯云<–/官网,轻量型云服务器低至112元/年,优惠多多。(联系我有折扣哦)

引入

上节中我们学习了多线程的概念和控制,但是实际上多线程的程序会有很多问题的出现。

首先我们看一个多线程程序的例子,一个模拟抢票的程序

#include <iostream>
#include <unistd.h>
#include "Thread.hpp"

int ticket = 10000;

void *getTicket(void *arg) // 执行抢票的逻辑
{
   
    while (true)
    {
   
        if (ticket > 0) // 当票量大于0的时候才能抢
        {
   
            usleep(1245); // 模拟抢票前执行的操作
            std::cout << static_cast<const char *>(arg) << "正在抢票" << ticket-- << std::endl; // 抢票
        }
        else // 如果票量小于0就不抢了
        {
   
            break;
        }
    }
    return nullptr;
}

int main()
{
   
    // 创建4个线程用于抢票
    Thread *thread1 = new Thread(getTicket, (void *)"thread-1");
    Thread *thread2 = new Thread(getTicket, (void *)"thread-2");
    Thread *thread3 = new Thread(getTicket, (void *)"thread-3");
    Thread *thread4 = new Thread(getTicket, (void *)"thread-4");

    thread1->join();
    thread2->join();
    thread3->join();
    thread4->join();

    return 0;
}

image-20240130152957444

这里可以看出多线程的程序出现了一些问题,那么怎么解决呢?

1. Linux线程互斥

1.1 互斥的相关概念

  • 临界资源:在计算机中存在着很多共享资源,这些资源是会被很多线程、进程共享的,为了保证这些资源的安全,所以需要被保护起来,这些被保护起来的共享资源就是临界资源

  • 临界区:临界资源被访问总是存在访问这些临界资源的代码,这些访问临界资源的代码叫做临界区

  • 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用

  • 原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成,不会有中间状态

1.2 互斥量mutex

现在我们来分析引入中的问题产生的原因:

image-20240130155511793

我们看上面的抢票逻辑的代码,在第11行的地方访问了共享资源ticket

线程1在执行了第11行的代码之后,线程可不可以被切换?当然可以

线程1在此时被切换之后,(我们把情况推向极端,此时ticket就是1),然后线程2被切换过来执行

此时ticket还是1然后执行11行代码进入if的代码块执行完本次循环之后再切回线程1

此时线程1可以直接执行14行代码,将ticket–,此时就出现了抢到-1的情况

这就是上述问题产生的原因,那么如何解决呢?

我们在上面说到了原子性,对于一件事,要么全做,要么不做,这里如果让11行到吗到14行代码变成原子性的,也就能够让这个问题得到解决

使用互斥量mutex就能完成这个任务


我们知道,一般来说,一条汇编指令就是原子的,但是像++i或者i++都不是原子的,有可能会有数据一致性问题

这是因为这种操作对应了三条汇编指令:

  • load:将共享变量i从内存加载到寄存器中
  • updata:更新寄存器里面i的值
  • store:将新的值从寄存器协会i的内存地址

那如果要解决以上问题,就需要做到三点

  • 代码必须要有互斥行为:代码进入临界区执行的时候,不允许其他线程进入该临界区
  • 如果多个线程通知要求执行临界区代码,并且临界区没有其他进程在执行,那么只能允许一个线程进入该临界区
  • 如果线程不再临界区中执行,那么该线程不能组织其他线程进入临界区
image-20240131161805208

1.3 mutex的使用

mutex也是pthread库提供的。形象的来说,我们也可以把它叫做锁。所以也就有了互斥锁这种说法

1. 互斥锁的类型

image-20240131152814709

2. 定义初始化与销毁

image-20240131153440920

头文件:
#include <pthread.h>
函数原型:
int pthread_mutex_destory(pthread_mutex_t *mutex); // 销毁
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexa
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凌云志.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值