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

解决死锁

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

相关推荐
小李独爱秋2 小时前
模拟面试:说一下数据库主从不同步的原因。
运维·服务器·mysql·面试·职场和发展·性能优化
白云偷星子2 小时前
RHCSA笔记7
linux·笔记
tryCbest2 小时前
Linux常用命令V2026
linux·运维
茶杯梦轩3 小时前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
服务器·后端·面试
奋斗的蛋黄4 小时前
硬链接(Hard Link)与软连接(Symbolic Link)核心解析
linux
先做个垃圾出来………4 小时前
DeepDiff差异语义化特性
服务器·前端
i建模4 小时前
Omarchy挂载windows磁盘
linux·运维·windows
林姜泽樾4 小时前
linux基础第一章,linux的介绍和它和windows的区别
linux·运维·计算机网络·安全·网络安全
nudt_qxx4 小时前
Ubuntu 26.04 LTS“坚毅浣熊”(Resolute Raccoon) 新特性前瞻
linux·数据库·ubuntu