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 占有的资源,每个进程都在链中等待下一个进程释放资源。

解决死锁

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

相关推荐
2301_777998344 小时前
Linux信号机制
linux
一叶龙洲7 小时前
win11与Ubuntu之间同步配置、插件
linux·运维·ubuntu
CHANG_THE_WORLD8 小时前
12.总结:深入理解 Linux I/O 多路复用:select、poll、epoll 全解析
linux·运维·服务器
风曦Kisaki8 小时前
# Linux笔记:操作系统优化与资源管理
linux·运维·服务器·笔记
Mr.HeBoYan9 小时前
一次持续三天才出现的丢包故障——深入解析 DPDK Memory Ordering、rte_ring 与 CPU Memory Barrier (下)
linux·网络·算法·架构·dpdk
zhangrelay10 小时前
笔记本轻量高品质延寿工具完整分系统清单
运维·笔记·学习
初願致夕霞10 小时前
C++懒汉单例设计详解
服务器·开发语言·c++
Discipline~Hai11 小时前
ARM01-ARM体系架构
linux·c语言·arm开发·架构
RisunJan11 小时前
Linux命令-screen(终端复用器)
linux·运维
啊哦11112 小时前
安装VMware Workstation
linux·运维·服务器