关于线程本人力推苏丙榅老师(曾经担任黑马讲师)的视频讲解,B站与老师自己博客都有老师的讲解,内容通俗易懂非常适合学习,本人线程学习皆从老师处学习。

https://subingwen.cn/linux/thread-sync/

上一章节讲述了线程的创建与使用,本章节主要讲述线程同步;

 为什么要线程同步?

举个栗子,相信年轻一的的小伙伴都有想过,假如我银行卡内有300块,我提到支付宝的时候同一时间在ATM里取300,那是不是就能赚他300? 很遗憾,银行可不会给你钻空子,此时就引出了线程同步的概念

线程同步的概念:

很多小伙伴认为线程同步是执行的同步,实则不然;所谓线程同步,其实是多线程情况下对于内存中的共享资源进行访问修改的时候,使这个共享资源按顺序被修改,所谓的同步并不是多个线程同时对内存进行访问,而是按照先后顺序依次进行的。

其实使用原子变量也可以达到以上效果,而且不用开锁解锁,更加节省资源,有兴趣的小伙伴可以去了解一下,这里不过多赘述

1.1 同步方式

对于多个线程访问共享资源出现数据混乱的问题,需要进行线程同步。常用的线程同步方式有四种:互斥锁、读写锁、条件变量、信号量。所谓的共享资源就是多个线程共同访问的变量,这些变量通常为全局数据区变量或者堆区变量,这些变量对应的共享资源也被称之为临界资源。

1.2互斥锁

创建的锁对象中保存了当前这把锁的状态信息:锁定还是打开,如果是锁定状态还记录了给这把锁加锁的线程信息(线程 ID)。一个互斥锁变量只能被一个线程锁定,被锁定之后其他线程再对互斥锁变量加锁就会被阻塞,直到这把互斥锁被解锁,被阻塞的线程才能被解除阻塞。一般情况下,每一个共享资源对应一个把互斥锁,锁的个数和线程的个数无关。

// 初始化互斥锁
// restrict: 是一个关键字, 用来修饰指针, 只有这个关键字修饰的指针可以访问指向的内存地址, 其他指针是不行的
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
// 释放互斥锁资源
int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数:
mutex: 互斥锁变量的地址
attr: 互斥锁的属性,一般使用默认属性即可,这个参数指定为 NULL


加锁

int pthread_mutex_lock(pthread_mutex_t *mutex);//给代码段加锁

线程会判断当前锁的状态:

如果是上锁状态:线程会阻塞在此处

如果为未锁:线程加锁并记录锁状态,向下执行


尝试加锁

// 尝试加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);

线程会判断当前锁的状态:

如果是上锁状态:线程不会被阻塞,加锁失败直接返回错误号

如果为未锁:线程加锁并记录锁状态,向下执行


解锁

// 对互斥锁解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);

1.3读写锁

        读写锁相当于高级的互斥锁,就像svip与vip一样。

创建:

pthread_rwlock_t rwlock;

之所以称其为读写锁,是因为这把锁既可以锁定读操作,也可以锁定写操作。为了方便理解,可以大致认为在这把锁中记录了这些信息:

1.1 锁的状态:锁定 / 打开
1.2 锁定的是什么操作:读操作 / 写操作,使用读写锁锁定了读操作,需要先解锁才能去锁定写操         作,反之亦然。
1.3 哪个线程将这把锁锁上了

特点:1.使用读写锁的读锁锁定了临界区,线程对临界区的访问是并行的,读锁是共享的。

           2.使用读写锁的写锁锁定了临界区,线程对临界区的访问是串行的,写锁是独占的。

           3.使用读写锁分别对两个临界区加了读锁和写锁,两个线程要同时访问者两个临界区,访问写锁临界区的线程继续运行,访问读锁临界区的线程阻塞,因为写锁比读锁的优先级高。

加读锁:

// 在程序中对读写锁加读锁, 锁定的是读操作
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

加写锁:

// 在程序中对读写锁加写锁, 锁定的是写操作
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

解锁:

// 解锁, 不管锁定了读还是写都可用解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

1.4条件变量

一般情况下条件变量主要针对消费者模型,并且和互斥锁配合使用

  • 严格意义上来说,条件变量的主要作用不是处理线程同步,而是进行线程的阻塞。如果在多线程程序中只使用条件变量无法实现线程的同步,必须要配合互斥锁来使用。
  • 条件变量只有在满足指定条件下才会阻塞线程,如果条件不满足,多个线程可以同时进入临界区,同时读写临界资源,这种情况下还是会出现共享资源中数据的混乱。
pthread_cond_t cond;//创建条件变量
// 初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
// 销毁释放资源
int pthread_cond_destroy(pthread_cond_t *cond);

参数

  • cond为对象地址
  • attr一般情况下置空,使用默认属性        

同步

        

// 线程阻塞函数, 哪个线程调用这个函数, 哪个线程就会被阻塞
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

若条件变量在此处阻塞,会自动打开传入的互斥锁,以便其他的线程正常运行,理论上来说可以使全部的消费者线程阻塞在此处,当生产者线程抢到cpu时间片后正常加锁执行后调用解除阻塞函数;使消费者线程正常执行,此时阻塞在此处的消费者线程会抢夺此处的互斥锁,如何正常执行,如果没抢到锁,则继续阻塞;

解除阻塞:

// 唤醒阻塞在条件变量上的线程, 至少有一个被解除阻塞
int pthread_cond_signal(pthread_cond_t *cond);
// 唤醒阻塞在条件变量上的线程, 被阻塞的线程全部解除阻塞
int pthread_cond_broadcast(pthread_cond_t *cond);

此文章为作者学习时的记录,如有缺陷多多包涵

Linux下的 c 多线程与线程同步(二)相关推荐

  1. linux使用线程实现生产者消费者问题,Linux下生产者与消费者的线程实现

    代码见<现代操作系统> 第3版. 为了显示效果,添加了printf()函数来显示运行效果 #include #include #define MAX 20 pthread_mutex_t ...

  2. 3、Linux多线程,线程同步(转)

    3.Linux多线程,线程同步 5)线程私有数据 进程内的所有线程共享进程的数据空间,因此全局变量为所有线程所共有.但有时线程也需要保存自己的私有数据,这时可以创建线程私有数据(Thread-spec ...

  3. linux下最简单多线程单文件socks5代理proxy服务器程序(仅一个c文件,带详细注解)

    2020.9.11 网上看的都比较复杂,GITHUB找了一个多线程的相对简单的MicroSocks项目,花了几周时间(没办法,菜鸟一个)改了一下,改成单文件,测试OK. /* wxl_socks5_p ...

  4. VC++ MFC 多线程及线程同步(详细、全面总结!)

    更多详情:http://blog.csdn.net/whyacinth/ VC++ MFC 多线程及线程同步 关键词: MFC    多线程及线程同步                          ...

  5. Java多线程之线程同步机制(锁,线程池等等)

    Java多线程之线程同步机制 一.概念 1.并发 2.起因 3.缺点 二.三大不安全案例 1.样例一(模拟买票场景) 2.样例二(模拟取钱场景) 3.样例三(模拟集合) 三.同步方法及同步块 1.同步 ...

  6. Linux下Rsync+Inotify-tools实现数据实时同步

    说明: 操作系统:CentOS 5.X 源服务器:192.168.21.129 目标服务器:192.168.21.127,192.168.21.128 目的:把源服务器上/home/www.osyun ...

  7. Linux下的主辅DNS服务器同步

    Linux下的主辅DNS服务器同步 一.系统环境介绍 二.辅助DNS搭建 1.安装yum包 2.设置服务自启 3.编辑dns主配置文件 4.编辑区域文件 5.配置正向文件 6.配置反向文件 7.重启服 ...

  8. Linux系统编程(九)线程同步

    Linux系统编程(九)线程同步 一.什么是线程同步? 二.互斥量 三.条件变量 pthread_cond_wait函数 pthread_cond_signal函数 生产者和消费者模型 一.什么是线程 ...

  9. C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent

    C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent 本章概要: 1:终止状态和非终止状态 2:AutoResetEvent和ManualResetE ...

最新文章

  1. 爱送礼成中国好前任,谢谢你让我认识了快递员
  2. js 排列 组合 的一个简单例子
  3. npm更改为淘宝镜像
  4. WinCE 开始菜单StartMenu_Create()函数代码分析
  5. mysql建表时主键_mysql建表时怎么设置主键?
  6. 基于安全压缩感知的大数据隐私保护
  7. mysql 傻瓜式管理_傻瓜式教学【数据库管理工具Navicat】
  8. 视频抽帧并存图 python_使用Python实现跳帧截取视频帧
  9. 限时下载 | 132G编程资料:Python、JAVA、C,C++、机器人编程、PLC,入门到精通~
  10. python open函数关于w+ r+ 读写操作的理解
  11. Android虚拟电脑,如何让你的android模拟器连接上你电脑的网络
  12. mikrotikROS路由配置L2TP
  13. I Sold Out for the Cash - 2022/8/10
  14. Linux学习-账户管理
  15. 免费获取对方ip地址PHP源码
  16. 铁威马F2-NAS2评测(家用云存储NAS)
  17. 【Python魔术方法】py复习
  18. java搭建直播平台
  19. mt4的服务器在哪个文件,mt4的服务器地址
  20. Parsec测试基准程序在zynq板上测试

热门文章

  1. docker启动nginx及常见nginx配置
  2. 根据长句,不够补位的JS方法
  3. 2022-2028全球与中国赌场游戏设备市场现状及未来发展趋势
  4. 物理像素,设备独立像素,像素比
  5. java中join_java中join的使用
  6. 从OSI七层看浏览器访问过程七层各自都有哪些协议
  7. 图谱实战 | 深度应用驱动的医学知识图谱构建
  8. 两个服务器之间项目通过nginx内网映射
  9. Linux Ubantu 常用指令
  10. Linux排查错误的命令,运维必备:常见的Linux系统故障及其排查的方法