当前位置: 首页 > article >正文

014 Linux_同步

​🌈个人主页:Fan_558
🔥 系列专栏:Linux
🌹关注我💪🏻带你学更多操作系统知识

在这里插入图片描述

文章目录

  • 前言
    • 一、死锁
      • (1)死锁概念
    • 二、同步
      • (1)同步概念
      • (2)条件变量
      • (3)函数接口
      • (4)代码实例
  • 小结

前言

本文将会向你介绍死锁的概念,以及同步的概念和实现

一、死锁

(1)死锁概念

死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所占用不会释放的资源而处于的一种永久等待状态。

举个例子,F1与F2去买奶茶,此时F1和F2手中都有5块钱,奶茶需要十块钱,F1伸手向F2要钱,F2不肯,并说你把钱给我,F1自然也是不愿意的,那么他们手中都有五元钱,又互相申请对方的五元钱,双方都不肯放手

死锁四个必要条件

互斥条件:一个资源每次只能被一个执行流使用 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺
循环等待条件:若干执行流之间形成一种头尾相接的循环 等待资源的关系

避免死锁

破坏死锁的四个必要条件
加锁顺序一致
避免锁未释放的场景
资源一次性分配

在这里插入图片描述
Lock申请锁失败,线程会阻塞住,不会返回,trylock是申请锁的非阻塞版本

二、同步

(1)同步概念

同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步(前文我们提到公共独立自习室的例子,若一个人长时间持有钥匙,当他出门并把钥匙重新放回原处的时候,看到一大堆人在等他出来,他不想失去这个自习室,于是他又进去了,他那么轻松再次进去的原因就是他距离钥匙更近些,对钥匙的竞争力更大,线程也是如此)。若是其它线程长时间不能得到共享资源,势必会导致饥饿问题
前一篇文章我们也提到:定一个规矩:出来的人,不能立马重新申请锁,想要继续申请,必须排到队列的最后面,外面来的,必须排队

(2)条件变量

要么继续申请锁要么退出,不管要干什么做后续工作之前,必须把铃铛敲一下
把铃铛和队列称为条件变量(提供一种简单的通知机制:唤醒线程,并提供一个队列
让线程在等待队列中排队)
在这里插入图片描述

(3)函数接口

1、初始化条件变量
静态分配:

pthread_cond_t cond=PTHREAD_COND_INITIALIZER

注意:静态分配的条件变量不需要手动销毁

动态分配:

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict
attr);
参数:
cond:要初始化的条件变量
attr:NULL

2、利用条件变量等待资源就绪

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量,后面详细解释

3、资源就绪后,主线程唤醒新线程来访问共享资源

int pthread_cond_broadcast(pthread_cond_t *cond); 
int pthread_cond_signal(pthread_cond_t *cond)

4、销毁

int pthread_cond_destroy(pthread_cond_t *cond)

(4)代码实例

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <stdint.h>

int cnt = 0;
//初始化锁、条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void* Count(void* args)
{
    //线程分离
    pthread_detach(pthread_self());
    uint64_t number = (uint64_t)args;
    while(true)
    {
    	//加锁
        pthread_mutex_lock(&mutex);
        //等待
        pthread_cond_wait(&cond, &mutex);
        std::cout << "pthread: " << number << ", cnt: " << cnt++ << std::endl;
        //解锁
        pthread_mutex_unlock(&mutex);
        sleep(3);
    }
}
int main()
{
    for(uint64_t i = 0; i < 5; i++)
    {
        pthread_t tid;
        //创建线程
        pthread_create(&tid, NULL, Count, (void*)i);
    }
    while(true)
    {
        sleep(1);
        //唤醒等待队列头部的线程
        //std::cout << "It's time to get up: one thread" << std::endl;
        //pthread_cond_signal(&cond);
        std::cout << "It's time to get up: all thread" << std::endl;
        pthread_cond_broadcast(&cond);
    }
    return 0;
}

这段代码的逻辑本质是这样

加锁-----------
判断共享资源是否就绪
while(不满足){
>等待资源就绪}
>执行接下来的代码
code...
...
解锁-----------

三个疑问:

一、为什么判断条件要放在加锁解锁(临界资源里)之间 原因: 可是我们怎么知道我们要让一个线程去挂起呢(一旦调用该接口pthread_cond_wait(&cond, &mutex)线程就挂起了? 一定是临界资源不就绪,你怎么知道临界资源是就绪还是不就绪呢?
你判断出来的 怎么判断出来的呢?
那一定是有访问该临界资源,否则怎么知道就绪还是不就绪呢。 那么为了防止多并发问题,那么我们是不是应该将会访问临界资源的判断加锁呢?
必须是的,也就是判断必须在加锁之后

二、为什么pthread_cond_wait也要放在加锁和解锁之间
因为当条件不满足的时候就需要去等待

三、pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t
*restrict mutex)为什么第二个参数携带锁

因为当一个线程进到临界区内时,当资源不就绪,该线程就应该去等待,可是如果该线程不释放这把锁就去等待的话,那么下一个线程该如何进入临界区内呢,锁只有一把,所以pthread_cond_wait第二个参数传入一把锁的原因是为了让线程去等待前释放锁

答疑完了,让我们来看看运行结果如何
每次唤醒一个线程,各个线程按照特定的次序访问这批资源
在这里插入图片描述
唤醒所有线程
在这里插入图片描述

小结

今日的分享就到这里啦,如果本文存在疏漏或错误的地方还请您能够指出!


http://www.kler.cn/a/274204.html

相关文章:

  • 第22天:信息收集-Web应用各语言框架安全组件联动系统数据特征人工分析识别项目
  • YOLO模型格式转换:pt -> onnx -> rknn
  • 【文档搜索引擎】搜索模块的完整实现
  • 在linux系统的docker中安装GitLab
  • 谷歌浏览器的网络安全检测工具介绍
  • Spring Boot 应用开发入门(一)
  • Linux下使用ntpdate进行时间同步
  • 8.python中的元组
  • Java13_反转字符串中的单词 III(方法二String转换成字符数组)
  • Java的图书管理系统,确实有两把斧子 ! ! !
  • 网络——入门基础
  • ES代替品:轻量级搜索引擎MeiliSearch
  • HarmonyOS NEXT应用开发—投票动效实现案例
  • HarmonyOS NEXT应用开发之多文件下载监听案例
  • 基础小白快速入门web前端开发技术----------->htm基础
  • C++ 引用变量、引用形参
  • 鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Toggle)
  • 高效日志为服务器保驾护航
  • sparksql简介
  • mysql查询条件包含IS NULL、IS NOT NULL、!=、like %* 、like %*%,不能使用索引查询,只能使用全表扫描,是真的吗???
  • bitset详解
  • 代理IP品质对Tik Tok代理的重要性
  • Vue快速教程:如何优雅地移除数组中的特定元素?
  • 架起桥梁,畅享流通:如何使用私有Registry实现镜像跨源同步与管理
  • linux系统中的PS命令详解
  • R语言中的常用基础绘图函数 直方图,箱线图,条形图,散点图