linux:线程同步

news/2024/4/28 16:51:16/文章来源:https://blog.csdn.net/li209779/article/details/137061437

在这里插入图片描述

个人主页 : 个人主页
个人专栏 : 《数据结构》 《C语言》《C++》《Linux》

文章目录

  • 前言
  • 线程同步
    • 条件变量接口
    • 简单示例
      • pthread_cond_wait为什么要有mutex
      • 伪唤醒问题的解决 (if->while)
  • 总结


前言

本文作为我对于线程同步知识总结


线程同步

  • 同步:在保证数据安全的前提下,让线程能够按照某种顺序访问临界资源,从而有效避免饥饿问题,叫做同步
  • 竞态条件:是指多个线程同时访问系统共享资源时,由于其执行顺序或时间上的不确定性,导致数据不一致或其它不可预料的结果(一般发生在对称多处理环境,中断和异常处理,内核态抢占,并发执行…)

看了上面两个概念,你可能还是不太理解同步是什么,为什么要有同步。下面我们就举一个例子。
我们假定有两个人,一个人A将苹果放在桌子上,另一个人B蒙着眼睛去桌子上拿苹果,桌子每次只允许有一个人,因为B不清楚桌子上的情况是什么(有一个苹果,没有苹果,桌子上全是苹果),那为了保险起见,B只能疯狂的去桌子上拿苹果,以保证B拿到所有的苹果。那如果桌子上没有苹果,B仍然疯狂去桌子上拿苹果,此时A是不是就不能去桌子上放苹果,那此时B是不是再做无用工,而且导致了A不能放苹果,B拿苹果的效率降低。如果我们将A,B换成线程,苹果看出共享资源(某种任务),桌子看成临界区,那B是不是就是一直在做申请锁,再释放锁的无效工作,并且导致了A的饥饿问题。这时我们就需要保证A,B之间的顺序问题,如在B访问过桌子后,不能立即再次访问桌子,要等待A访问桌子后。这是不是就会使A,B拿苹果的效率提升。而这就是为什么要有同步的理由

条件变量接口

初始化条件变量

  • 静态初始化在这里插入图片描述
    与互斥锁类似,定义一个全局的条件变量,用PTHREAD_COND_INITIALIZER宏来初始化,系统自动释放该条件变量。该宏一般存放在 /usr/include/pthread.h 路径下
    在这里插入图片描述
  • 动态初始化
    在这里插入图片描述
    restrict是C语言的一个关键字,表示该指针是唯一的访问其指向对象的指针。
    cond将被初始化的条件变量,attr 为nullptr,使用默认属性初始化条件变量。
    如果函数成功执行,返回0; 如果函数执行失败,则返回错误码,如EAGAIN(资源暂时不可用),ENOMEM(内存不足)

销毁条件变量
在这里插入图片描述
cond 表示将被销毁的条件变量,需要注意在调用pthread_cond_destroy后,该指针本身并未被销毁,只是所指向的条件变量被销毁,记得将指针置空
如果函数成功执行,返回0; 如果函数失败,返回错误码。如EBUSY,该条件变量正在被使用


等待条件变量
在这里插入图片描述
cond 表示将要等待的条件变量,mutex 表示线程所持有的互斥锁
如果函数成功执行,返回0;如果函数执行失败,返回错误码,如EINVAL 无效的参数(条件变量 or 互斥锁为初始化…)

关于该函数的参数为什么会有锁,有什么注意事项,在下面代码示例,我们再解释。


唤醒等待

在这里插入图片描述
cond要发生信号的条件变量指针。
如果函数执行成功,返回0; 如果函数执行失败,返回错误码,如ENVAL(无效的参数)
需要注意的是,pthread_cond_signal只用于唤醒在cond条件变量的阻塞队列中等待的一个线程。

在这里插入图片描述
该函数的参数与返回值与pthread_cond_signal相同,只不过该函数唤醒所有在cond条件变量的阻塞队列中等待的所有线程,而那些线程先执行,取决于操作系统的调度策略。


简单示例

我们先来看看下面代码,3个线程争夺ticket资源。当三个线程检测到ticket == 0时,三个线程都将等待。我们主线程每过5秒,使ticket += 10。

#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>using namespace std;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int ticket = 100;void* threadRoutine(void *args)
{const string threadname = static_cast<char*>(args);while(true){   usleep(1000);pthread_mutex_lock(&mutex);if(ticket > 0){ticket--;cout << threadname << ", get a ticket: " << ticket << endl;}else{cout << threadname << ", ticket == 0" << endl;pthread_cond_wait(&cond, &mutex);}pthread_mutex_unlock(&mutex);}return nullptr;
}int main()
{pthread_t td1;pthread_create(&td1, nullptr, threadRoutine, (void*)"thread-1");pthread_t td2;pthread_create(&td2, nullptr, threadRoutine, (void*)"thread-2");pthread_t td3;pthread_create(&td3, nullptr, threadRoutine, (void*)"thread-3");while(true){sleep(5);pthread_mutex_lock(&mutex);ticket += 10;pthread_mutex_unlock(&mutex);pthread_cond_signal(&cond);}pthread_join(td1, nullptr);pthread_join(td2, nullptr);pthread_join(td3, nullptr);return 0;
}

在这里插入图片描述
当线程2,线程3,线程1先后检测到ticket == 0时,在cond的等待队列中,线程以2,3,1的顺序排队。那当调用pthread_cond_signal函数时,线程2会先执行,执行完再检测到ticket==0,排到等待队列尾部
在这里插入图片描述
再调用pthread_cond_signal函数时,线程3会执行。
在这里插入图片描述
再调用pthread_cond_signal函数时,线程1会执行。
在这里插入图片描述
这样,我们多线程就可以按某种特定顺序来执行。
那如果我们使用pthread_cond_broadcast函数,会有什么情况?
在这里插入图片描述
我们会发现,三个线程都被唤醒,来争抢ticket资源,其先后顺序由操作系统决定(优先级,竞争锁的能力…)。


pthread_cond_wait为什么要有mutex

此时不知道你是否有一个疑问,我们假定线程-1先争抢到ticket,检测到ticket == 0时,该线程-1要在cond的等待队列中等待,然后其它两个线程在进入临界区,检测到ticket==0,在等待队列中排队。但是线程-1是持有锁进入等待队列的,那其它两个线程是如何进入临界区的?答案很明显,那就是线程-1在cond等待中,一定释放了其持有的锁,从而使其余两个线程可以持有锁进入临界区。这就是为什么pthread_cond_wait函数的参数要有互斥锁的存在。
那我们在深入想一想,调用pthread_cond_wait函数后,该线程是先释放锁,再进入等待队列中;还是先进入等待队列中,再释放锁?答案是都不是。释放锁和进入等待队列是同时进行的!!!这也表示pthread_cond_wait函数是原子的,在调用pthread_cond_wait时,涉及的互斥锁释放和进入等待队列的操作是作为一个不可分割的整体来执行。确保了线程在调用该函数时不会遇到竞态条件,即线程在释放锁和进入等待队列之间不会被其它线程打断。
以下是线程调用pthread_cond_wait的过程

  1. 线程必须已经持有锁:在调用pthread_cond_wait时,线程必须已经锁定了某个互斥锁,这是该函数的前提条件

  2. 自动释放互斥锁:当线程调用pthread_cond_wait时,它会自动释放它当前持有的互斥锁。这一步是为了允许其它线程有机会获取该互斥锁并修改共享资源,从而可能改变条件变量的状态

  3. 加入等待队列:释放互斥锁之后,线程会接着被添加到条件变量的等待队列中,并在此处等待

  4. 等待被唤醒:线程在等待队列中等待,直到其它线程调用pthread_cond_signal 或 pthread_cond_broadcast来唤醒它

  5. 重新获取互斥锁:当线程被唤醒时,线程会尝试重新获取之前释放的互斥锁。如果锁此时没有被其它线程持有,线程将成功获取锁并继续执行。如果锁仍然被其它线程持有,线程将阻塞(在申请锁的地方阻塞),直到能够获取锁时。

具体来说,pthread_cond_wait函数的内部实现保证了2,3步骤的原子性。


伪唤醒问题的解决 (if->while)

伪唤醒问题是指:在多线程环境中,当线程等待某个条件变量时,它可能会在没有到达预期的情况下被唤醒。
对于这一问题,我们可以在判断的时候,将if 变为 while,使其被唤醒后任然进行条件判断,如果条件满足,线程继续向后执行,如果条件不被满足,线程继续在该条件变量下等待。

pthread_mutex_lock(&mutex);
// 访问临界区 
while(条件为假)pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);

总结

以上就是我对于线程同步的总结。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1027346.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

admin端

一、创建项目 1.1 技术栈 1.2 vite 项目初始化 npm init vitelatest vue3-element-admin --template vue-ts 1.3 src 路径别名配置 Vite 配置 配置 vite.config.ts // https://vitejs.dev/config/import { UserConfig, ConfigEnv, loadEnv, defineConfig } from vite im…

步态采集平台

&#x1f349;步骤一、读取视频每一帧图像 &#x1f349;步骤二、对读取的图像进行分割&#xff0c;得到全景下的步态轮廓图。 ​​​​​​​&#x1f349;步骤三、对读取的图像进行裁剪得到归一化的步态轮廓图。 ​​​​​​​&#x1f349;步骤四、保存这一帧步态轮廓图

【Web应用技术基础】CSS(5)——表格样式

第一题&#xff1a;表格边框 .html <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>HTML – 简单表格</title><link rel"stylesheet" href"step1/CSS/style.css"></head><bod…

用 AI 编程-释放ChatGPT的力量

最近读了本书&#xff0c;是 Sean A Williams 写的&#xff0c;感觉上还是相当不错的。一本薄薄的英文书&#xff0c;还真是写的相当好。如果你想看&#xff0c;还找不到&#xff0c;可以考虑私信我吧。 ChatGPT for Coders Unlock the Power of AI with ChatGPT: A Comprehens…

html音频和视频可输入表单input

音频和视频 loop循环播放autoplay自动播放controls显示控制面板<audio src""> //<video src"#">muted静音播放 可输入表单input password密码框 radio单选框 checkbox复选框 file上传文件 text文本框 文本框<input type"text"…

Mysql数据库-DQL查询

Mysql数据库-DQL基本查询 1 DQL基本查询1.1 基础查询1.2 WHERE子句1&#xff09;算术运算符2&#xff09;逻辑运算符3&#xff09;比较运算符A&#xff09;BETWEEN... AND ...B&#xff09;IN(列表)C&#xff09;NULL值判断 4&#xff09;综合练习 2 DQL高级查询2.1 LIKE 模糊查…

Spring用到了哪些设计模式?

目录 Spring 框架中⽤到了哪些设计模式&#xff1f;工厂模式单例模式1.饿汉式&#xff0c;线程安全2.懒汉式&#xff0c;线程不安全3.懒汉式&#xff0c;线程安全4.双重检查锁&#xff08;DCL&#xff0c; 即 double-checked locking&#xff09;5.静态内部类6.枚举单例 代理模…

语音陪玩交友软件系统程序-app小程序H5三端源码交付,支持二开!

电竞行业的发展带动其周边产业的发展&#xff0c;绘制着游戏人物图画的抱枕、鼠标垫、海报销量极大&#xff0c;电竞游戏直播、游戏教程短视频也备受人们喜爱&#xff0c;自然&#xff0c;像游戏陪练、代练行业也随之生长起来&#xff0c;本文就来讲讲&#xff0c;从软件开发角…

CSS(二)---【常见属性、复合属性使用】

零.前言 本篇文章主要阐述CSS常见属性、复合属性&#xff0c;更多前置知识请见作者其它文章&#xff1a; CSS(一)---【CSS简介、导入方式、八种选择器、优先级】-CSDN博客 1.CSS属性 CSS的属性有上百个&#xff0c;但是我们并不需要全部学习&#xff0c;只要我们学习一部分…

3.28学习总结

java 封装 封装体现了java的面向对象的特点,用户不用知道程序是如何运行的,只用按照所给的格式输入参数,便可得到对应的结果. 一个完整的封装需要每个实例变量都用private来修饰并拥有相应的public getter和setter方法. 代码 public class girl {private int age;public st…

自然语言处理(NLP)全面指南

自然语言处理&#xff08;NLP&#xff09;是人工智能领域中最热门的技术之一&#xff0c;它通过构建能够理解和生成人类语言的机器&#xff0c;正在不断推动技术的发展。本文将为您提供NLP的全面介绍&#xff0c;包括其定义、重要性、应用场景、工作原理以及面临的挑战和争议。…

制作一个RISC-V的操作系统七-UART初始化(UART NS16550A 规定 目标 发送数据 代码 extern)

文章目录 UARTNS16550A规定目标发送数据代码extern UART 对应到嵌入式开发中&#xff0c;qemu模拟的就是那块开发板&#xff08;硬件&#xff09; 电脑使用qemu时可以理解为qemu模拟了那块板子&#xff0c;同时那块板子与已经与你的电脑相连接了&#xff08;我们对应的指定的内…

【零基础C语言】编译和链接

1.翻译环境和运行环境 翻译环境&#xff1a;将源代码转化为可执行的机器指令 运行环境&#xff1a;用于执行机器指令 1.1 翻译环境 翻译环境由编译和链接两大过程构建&#xff0c;编译又可以分为三大过程&#xff1a; 【1】预处理(预编译) 【2】编译 【3】汇编 不同的.c文件经…

虚拟机-从头配置Ubuntu18.04(包括anaconda,cuda,cudnn,pycharm,ros,vscode)

最好先安装anaconda后cuda和cudnn&#xff0c;因为配置环境的时候可能conda会覆盖cuda的路径&#xff08;不确定这种说法对不对&#xff0c;这里只是给大家的建议&#xff09; 准备工作&#xff1a; 1.Ubuntu18.04&#xff0c;x86_64&#xff0c;amd64 虚拟机下载和虚拟机Ubu…

【氮化镓】位错对氮化镓(GaN)电子能量损失谱(EEL)的影响

本文献《Influence of dislocations on electron energy-loss spectra in gallium nitride》由C. J. Fall等人撰写&#xff0c;发表于2002年。研究团队通过第一性原理计算&#xff0c;探讨了位错对氮化镓&#xff08;GaN&#xff09;电子能量损失谱&#xff08;EEL&#xff09;…

探索 2024 年 Web 开发最佳前端框架

前端框架通过简化和结构化的网站开发过程改变了 Web 开发人员设计和实现用户界面的方法。随着 Web 应用程序变得越来越复杂&#xff0c;交互和动画功能越来越多&#xff0c;这是开发前端框架的初衷之一。 在网络的早期&#xff0c;网页相当简单。它们主要以静态 HTML 为特色&a…

ArcGIS Pro横向水平图例

终于知道ArcGIS Pro怎么调横向图例了&#xff01; 简单的像0一样 旋转&#xff0c;左转右转随便转 然后调整图例项间距就可以了&#xff0c;参数太多就随便试&#xff0c;总有一款适合你&#xff01; 要调整长度&#xff0c;就调整图例块的大小。完美&#xff01; 好不容易…

大型矿业集团安全知识竞赛主持词

男&#xff1a;尊敬的各位领导&#xff0c;员工同志们&#xff1a; 合&#xff1a;大家好&#xff01; 男&#xff1b;首先让我们以热烈的掌声对公司领导亲临比赛现场指导观看表示欢迎&#xff01; 男&#xff1b;继成功开展了荣辱观专题讲座、好矿嫂女红艺术展、安全谜语竞猜…

ArcGIS制作风向频率玫瑰图

风玫瑰图是气象科学专业统计图表,用来统计某个地区一段时期内风向、风速发生频率,又分为“风向玫瑰图”和“风速玫瑰图” ;因图形似玫瑰花朵,故名。风玫瑰图对于涉及城市规划、环保、风力发电等领域有着重要的意义。风玫瑰图能够直观的显现某地区不同方位风向的频率特征,进…

Python拆分PDF、Python合并PDF

WPS能拆分合并&#xff0c;但却是要输入编辑密码&#xff0c;我没有。故写了个脚本来做拆分&#xff0c;顺便附上合并的代码。 代码如下&#xff08;extract.py) #!/usr/bin/env python """PDF拆分脚本(需要Python3.10)Usage::$ python extract.py <pdf-fil…