Linux——线程互斥,互斥锁

目录

线程同步与互斥

抢票的问题

相关概念

互斥

互斥锁

锁的创建

上锁

释放锁

销毁锁

解决抢票的问题

死锁

死锁的原因

形成死锁的四个条件

解决死锁


只有认知的突破 💫才能带来真正的成长 💫编程技术的学习 💫没有捷径 💫一起加油💫

🍁感谢各位的观看 🍁欢迎大家留言 🍁咱们一起加油 🍁努力成为更好的自己🍁

线程同步与互斥

抢票的问题

用多个线程模拟多个用户抢票,如果对于公共资源不加保护,就会出现严重的问题。如下所示的代码。

cpp 复制代码
// 操作共享变量会有问题的售票系统代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

int ticket = 100;   //总票数

void *route(void *arg)
{
    char *id = (char *)arg;
    while (1)
    {
        if (ticket > 0)
        {
            usleep(1000);
            printf("%s sells ticket:%d\n", id, ticket);
            ticket--;
        }
        else
        {
            break;
        }
    }
}
int main(void)
{
    pthread_t t1, t2, t3, t4;   //创建4个线程
    pthread_create(&t1, NULL, route, (void *)"thread 1");
    pthread_create(&t2, NULL, route, (void *)"thread 2");
    pthread_create(&t3, NULL, route, (void *)"thread 3");
    pthread_create(&t4, NULL, route, (void *)"thread 4");
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_join(t3, NULL);
    pthread_join(t4, NULL);
    return 0;
}

**现象:**票被抢到负数!!!。这就是没有对共享资源的保护!!!

相关概念

  • 共享资源 。

  • 临界资源:临界区对应的资源,叫做临界资源。

  • 临界区:每个线程内部,访问临界资源的代码,就叫做临界区。

  • 非临界区:非访问临界资源的代码,叫做非临界区。

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

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

如下图所示。

互斥

互斥锁

互斥锁可以解决上面抢票的问题。

过程:每个线程在抢票的时候,先申请一把锁,谁有锁,才可以抢票,抢完票后再把锁释放,下一个线程再申请锁......。反复这个过程,即可避免抢票的问题。

如下图所示。

锁的创建
  • 静态锁的创建------创建一个全局锁(变量)。

代码:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

cpp 复制代码
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int main()
{
    //........//
    return 0;
}
  • 动态锁的创建------创建一个局部锁(变量)。

    • 函数: int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr),动态锁的创建,需要使用该函数进行初始化。

    • **参数:**mutex:要初始化的互斥锁。attr:NULL。

cpp 复制代码
int main()
{
    pthread_mutex_t mutex;    //创建锁
    pthread_mutex_init(&mutex, NULL);    //锁的初始化
    //........//
    return 0;
}
上锁

函数:int pthread_mutex_lock(pthread_mutex_t *mutex)。

释放锁

函数:int pthread_mutex_unlock(pthread_mutex_t *mutex)。

销毁锁

函数:int pthread_mutex_destroy(pthread_mutex_t *mutex)。

解决抢票的问题

票就是临界资源,对临界资源加锁,即可解决问题,如下所示的代码。

cpp 复制代码
// 操作共享变量会有问题的售票系统代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int ticket = 10;   //总票数
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *route(void *arg)
{
    char *id = (char *)arg;
    while (1)
    {
        pthread_mutex_lock(&mutex); //加锁
        if (ticket > 0)
        {
            sleep(1);
            printf("%s sells ticket:%d\n", id, ticket);
            ticket--;
            pthread_mutex_unlock(&mutex);//释放锁
        }
        else
        {
            printf("%s sells ticket:%d\n", id, ticket);
            break;
        }
    }
    //避免抢到票为0,锁未释放
     pthread_mutex_unlock(&mutex);//释放锁
    return (void*)0;
}
int main(void)
{
    pthread_t t1, t2, t3, t4;   //创建4个线程
    pthread_create(&t1, NULL, route, (void *)"thread 1");
    pthread_create(&t2, NULL, route, (void *)"thread 2");
    pthread_create(&t3, NULL, route, (void *)"thread 3");
    pthread_create(&t4, NULL, route, (void *)"thread 4");
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_join(t3, NULL);
    pthread_join(t4, NULL);

    //等待完所有的线程后,进行锁的销毁
     pthread_mutex_destroy(&mutex);
    return 0;
}

注意 :虽然保证了资源的安全,但是出现了一个线程独霸一方的局面,其它线程无法得到有效的资源,这就造成了饥饿问题,为了解决这个问题就需要条件变量和互斥锁配合了。

死锁
死锁的原因

以两个线程为例子,线程A和线程B。线程A持有线程B的锁,线程B持有线程A的锁。线程A访问资源需要线程B持有的锁,而线程B访问资源需要线程A持有的锁,双方都需要对方的锁,双方不释放,就这样僵持着,就形成了死锁。如下所示。

形成死锁的四个条件
  • 互斥条件

资源具有排他性,同一时间只能被一个进程占用,其他进程想要使用该资源,必须等待当前占用进程释放。

  • 请求和保持条件(占有且等待)

进程已经占有了至少一个资源 ,又提出了新的资源请求,而新资源被其他进程占用,该进程会保持已占有的资源,同时等待新资源,且在等待期间不释放已占资源。

  • 不剥夺条件(非抢占条件)

进程所占有的资源不能被强制剥夺 ,只能由进程主动释放,其他进程无法强行抢占正在被占用的资源。

  • 循环等待条件

系统中存在一组进程 ,形成环形的资源等待链:进程 1 等待进程 2 占有的资源,进程 2 等待进程 3 占有的资源......,最后一个进程等待进程 1 占有的资源,每个进程都在链中等待下一个进程释放资源。

解决死锁

方法:破坏形成死锁四个条件的任意一个条件,就会破坏死锁。

相关推荐
AlfredZhao1 天前
生产环境里,为什么不建议把普通端口直接暴露到公网?
linux·https·443·80
戴为沐2 天前
Linux内存扩容指南
linux
zylyehuo2 天前
Linux 彻底且安全地删除文件
linux
用户805533698033 天前
主线 U-Boot 上 RK3506:和闭源 rkbin 拔河的三个隐性契约
linux·嵌入式
用户034095297913 天前
linux fcitx 5 雾凇拼音 设置在中文输入法下仍然输入英文标点
linux
乘云数字DATABUFF3 天前
5分钟部署开源APM Databuff:OpenTelemetry全链路追踪入门实战
运维·后端
Web3探索者5 天前
可视化服务器管理和传统命令行区别是什么?新手教程:Linux 运维到底该用图形界面还是 SSH 命令行?
linux·ssh
zylyehuo5 天前
Linux系统中网线与USB网络共享冲突
linux
荣--5 天前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森5 天前
动手实战学 Docker — 从零到集群编排完全指南
运维