前言:学进程时,学习的重点应该进程间通信,而学习线程时,重点就应该是线程同步了。想过为什么?fork创建子进程之后,子进程有自己的独立地址空间和PCB,想和父进程或其它进程通信,就需要各种通信方式,例如无名管道(管道,我习惯这么叫无名管道)、有名管道(命名管道)、信号、消息队列、信号量、共享内存等;而pthread_create创建子线程之后,子线程没有独立的地址空间,大部分数据都是共享的,如果同时访问数据,就是造成混乱,所以要控制,就是线程同步了。

  一、同步概念

  为什么要特意说一下同步概念呢?因为它跟其他领域的“同步”有些差异。

  所谓同步,即同时起步,协调一致。不同的对象,对“同步”的理解方式略有不同。如,设备同步,是指在两个设备之间规定一个共同的时间参考;数据库同步,是指让两个或多个数据库内容保持一致,或者按需要部分保持一致;文件同步,是指让两个或多个文件夹里的文件保持一致。等等

而,编程中、通信中所说的同步与生活中大家印象中的同步概念略有差异。“同”字应是指协同、协助、互相配合。主旨在协同步调,按预定的先后次序运行。

  二、线程同步方式

  这篇博客主要介绍四种方式,如下:

方式 通用标识
互斥锁(互斥量) pthread_mutex_
读写锁  pthread_rwlock_
条件变量 pthread_cond_
信号量 sem_

    表中的“通用标识”,指的是那种同步方式的函数、类型都那么开头的,方便记忆;还有其他方式,自旋锁、屏蔽,感觉不常用,有兴趣可以阅读APUE。

   三、互斥锁(互斥量)

  1、介绍

  先来画个图,来简单说明一下:  PS:依旧是全博客园最丑图,不接受反驳!

  

       

  Linux中提供一把互斥锁mutex(也称之为互斥量)。

  每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。

资源还是共享的,线程间也还是竞争的,

但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。

  2、主要函数  

  pthread_mutex_init函数    //初始化mutex,默认为1

pthread_mutex_destroy函数  //销毁锁

pthread_mutex_lock函数     //加锁,加锁不成功,一直阻塞在那等待

pthread_mutex_trylock函数   //尝试加锁,加锁不成功,直接返回

pthread_mutex_unlock函数  //解锁

  以上5个函数的返回值都是:成功返回0, 失败返回错误号。

  pthread_mutex_t 类型,其本质是一个结构体。为简化理解,应用时可忽略其实现细节,简单当成整数看待。

  变量mutex只有两种取值1、0。

  • pthread_mutex_init函数

  初始化一个互斥锁(互斥量) ---> 初值可看作1

原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

参1:传出参数,调用时应传 &mutex

这个restrict关键字可能第一次遇到,说明一下:只用于限制指针,告诉编译器,所有修改该指针指向内存中内容的操作,只能通过本指针完成。不能通过除本指针以外的其他变量或指针修改

参2:互斥量属性。是一个传入参数,通常传NULL,选用默认属性(线程间共享)。互斥锁也可以用于进程间同步,需要修改属性为进程间共享。 参APUE.12.4同步属性

  1. 静态初始化:如果互斥锁 mutex 是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。e.g.  pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER;
  2. 动态初始化:局部变量应采用动态初始化。e.g.  pthread_mutex_init(&mutex, NULL)

  其他函数就不解释了,相对比较简单。

  示例程序,主要对标准输出进行加锁,使主线程打印大写“HELLO WORLD”,子线程打印小写“hello world”,程序如下:

  

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>pthread_mutex_t mutex;void err_thread(int ret, char *str)
{if (ret != 0) {fprintf(stderr, "%s:%s\n", str, strerror(ret));pthread_exit(NULL);}
}void *tfn(void *arg)
{srand(time(NULL));while (1) {pthread_mutex_lock(&mutex);printf("hello ");sleep(rand() % 3);    /*模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误*/printf("world\n");pthread_mutex_unlock(&mutex);sleep(rand() % 3);}return NULL;
}int main(void)
{int flag = 5;pthread_t tid;srand(time(NULL));pthread_mutex_init(&mutex, NULL);pthread_create(&tid, NULL, tfn, NULL);while (flag--) {pthread_mutex_lock(&mutex);printf("HELLO ");sleep(rand() % 3);printf("WORLD\n");pthread_mutex_unlock(&mutex);sleep(rand() % 3);}pthread_cancel(tid);                //  将子线程杀死,子线程中自带取消点
    pthread_join(tid, NULL);pthread_mutex_destroy(&mutex);return 0;                           //main中的return可以将整个进程退出
}

View Code

  编译时也要记得链上-pthread。

  四、读写锁

  1、特性

  (1)读写锁是“写模式加锁”时, 解锁前,所有对该锁加锁的线程都会被阻塞。

  (2)读写锁是“读模式加锁”时, 如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。

  (3)读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高

读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。

读写锁非常适合于对数据结构读的次数远大于写的情况。

  敲重点了,记住12个字:写独占、读共享;写锁优先级高。

  2、主要函数  

   pthread_rwlock_init函数     //初始化

pthread_rwlock_destroy函数  //销毁锁

pthread_rwlock_rdlock函数    //读加锁,阻塞

pthread_rwlock_wrlock函数   //写解锁,阻塞

pthread_rwlock_tryrdlock函数  //尝试读解锁

pthread_rwlock_trywrlock函数 //尝试写加锁

pthread_rwlock_unlock函数  //解锁

  以上7 个函数的返回值都是:成功返回0, 失败直接返回错误号。

pthread_rwlock_t类型   用于定义一个读写锁变量。

pthread_rwlock_t rwlock;

  这些参考互斥锁的函数,进行对比学习,只是多了读锁和写锁,就不过多解释了。

  实例程序,3个线程“写”全局变量,5个全局变量“读”全局变量,程序如下:

  

/* 3个线程不定时 "写" 全局资源,5个线程不定时 "读" 同一全局资源 */#include <stdio.h>
#include <unistd.h>
#include <pthread.h>int counter;                          //全局资源
pthread_rwlock_t rwlock;void *th_write(void *arg)
{int t;int i = (int)arg;while (1) {t = counter;usleep(1000);pthread_rwlock_wrlock(&rwlock);printf("=======write %d: %lu: counter=%d ++counter=%d\n", i, pthread_self(), t, ++counter);pthread_rwlock_unlock(&rwlock);usleep(5000);}return NULL;
}void *th_read(void *arg)
{int i = (int)arg;while (1) {pthread_rwlock_rdlock(&rwlock);printf("----------------------------read %d: %lu: %d\n", i, pthread_self(), counter);pthread_rwlock_unlock(&rwlock);usleep(900);}return NULL;
}int main(void)
{int i;pthread_t tid[8];pthread_rwlock_init(&rwlock, NULL);for (i = 0; i < 3; i++)pthread_create(&tid[i], NULL, th_write, (void *)i);for (i = 0; i < 5; i++)pthread_create(&tid[i+3], NULL, th_read, (void *)i);for (i = 0; i < 8; i++)pthread_join(tid[i], NULL);pthread_rwlock_destroy(&rwlock);            //释放读写琐return 0;
}

View Code

  另两种方式,还有条件变量和信号量,条件变量比较难理解,篇幅比较多,所以会另写一篇博客来写,敬请期待哦! 

       

转载于:https://www.cnblogs.com/liudw-0215/p/9685498.html

你真的懂线程同步么?相关推荐

  1. 2万字,看完这篇才敢说自己真的懂线程池!

    前言 线程池可以说是 Java 进阶必备的知识点了,也是面试中必备的考点,可能不少人看了一些文章后能对线程池工作原理说上一二,但这还远远不够,如果碰到比较有经验的面试官再继续追问,很可能会被吊打,考虑 ...

  2. 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截...

    程序猿修仙之路--数据结构之你是否真的懂数组? 数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构  .要想在之后的江湖历练中通关,数据结构必不可少.数据 ...

  3. 面试官问你Java线程池--怎么样回答才能让面试官知道你真的懂了!

    一.引言 不管是Java面试还是Android面试,线程池都是面试官高频考察的点,那我们怎么回答,才能让面试官了解到我们是真的懂Java线程池了呢?这篇文章不涉及到线程池的使用和原理,如果你还不知道怎 ...

  4. 使用线程锁(lock)实现线程同步_一文搞懂Java多线程使用方式、实现原理以及常见面试题...

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  5. java线程 同步与异步 线程池

    1)多线程并发时,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线 程的处理的数据,而B线程又修改了A线程处理的数理.显然这是由于全局资源造成的,有时为了解 决此问题,优先考虑 ...

  6. 线程同步——内核对象实现线程同步——等待函数

    1 对于内核对象实现线程同步,不得不提三点: 2 1)大多数内核对象既有触发也有未触发两个状态 3 比如:进程.线程.作业.文件流.事件.可等待的计时器.信号量.互斥量 4 2)等待函数:等待函数使线 ...

  7. Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析

    1.简介 在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concurrent 包下的两个线程同步 ...

  8. Java线程同步:synchronized锁住的是代码还是对象

    在Java中,synchronized关键字是用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行.synchronized既可以加在一段代码上,也可以加在 ...

  9. C#线程同步(1)- 临界区&Lock

    文章原始出处 http://xxinside.blogbus.com/logs/46441956.html 预备知识:线程的相关概念和知识,有多线程编码的初步经验. 一个机会,索性把线程同步的问题在C ...

最新文章

  1. C语言程序设计省二考试,浙江省高校计算机等级考试大纲(二级——C语言程序设计大纲)(...
  2. STL之bitset
  3. 数字转字符函数_Excel之文本函数CONCATENATE/TEXT/LEFT/MID/RIGHT/FIND/LEN
  4. java统计系统线程数_Java并发(八)计算线程池最佳线程数
  5. win10 安装oracle11g R2的64位版本
  6. robbe+base64+Mysql简易有效的php全文索引实现
  7. python脚本 游戏赚金币_Python实现王者荣耀刷金币脚本功能
  8. MySQL面试夺命连环27问
  9. sklearn2onnx
  10. Cesium|xt3d 雷达追踪圆锥体
  11. win10常用dos命令
  12. 错过就要多花 200 元,Unite 2017 Shanghai 五折抢票倒计时!
  13. 某喷码机品牌U盘存储的配置文件简记
  14. Visualization领域研究以及会议分类
  15. 带你实现女朋友欲罢不能的网易云音乐宇宙尘埃特效
  16. IDEA项目初次上传到git(超简单)
  17. 计算机硬件安全措施,企业计算机硬件安全保障措施的探究
  18. 【矩阵论】3. 矩阵函数——矩阵函数求导
  19. lxy 3.0php网络验证,飘零网络验证系统金盾3.0WEB源码(和谐版)下载
  20. 《庄子》读书笔记(一)

热门文章

  1. C#常用类库----CSV文件操作类
  2. 《Pytorch - 神经风格转换》
  3. FCGF论文阅读笔记
  4. 计算机网络——访问网站数据传输过程
  5. 西瓜书——EM算法(一)
  6. 吴恩达深度学习 —— 2.5 导数
  7. redis入门——安装篇
  8. Bootstrap学习笔记
  9. mac下安装与配置mysql数据库,Mac下MySQL的安装与配置
  10. 大数据 数据库 评测_为什么腾讯QQ的大数据平台选择了这款数据库?