同步概念

所谓同步,即同时起步,协调一致。不同的对象,对“同步”的理解方式略有不同。如,设备同步,是指在两 个设备之间规定一个共同的时间参考;数据库同步,是指让两个或多个数据库内容保持一致,或者按需要部分保持 一致;文件同步,是指让两个或多个文件夹里的文件保持一致。等等
而,编程中、通信中所说的同步与生活中大家印象中的同步概念略有差异。“同”字应是指协同、协助、互相 配合。主旨在协同步调,按预定的先后次序运行。

线程同步

同步即协同步调,按预定的先后次序运行。
线程同步,指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其它线程为保证数据 一致性,不能调用该功能。

  1. 举例 1: 银行存款 5000。柜台,折:取 3000;提款机,卡:取 3000。剩余:2000

  2. 举例 2: 内存中 100 字节,线程 T1 欲填入全 1, 线程 T2 欲填入全 0。但如果 T1 执行了 50 个字节失去 cpu,T2 执行,会将 T1 写过的内容覆盖。当 T1 再次获得 cpu 继续 从失去 cpu 的位置向后写入 1,当执行结束,内存中的 100 字节,既不是全 1,也不是全 0。

    产生的现象叫做“与时间有关的错误”(time related)。为了避免这种数据混乱,线程需要同步。
    “同步”的目的,是为了避免数据混乱,解决与时间有关的错误。实际上,不仅线程间需要同步,进程间、信 号间等等都需要同步机制。
    因此,所有“多个控制流,共同操作一个共享资源”的情况,都需要同步。

数据混乱原因

  1. 资源共享(独享资源则不会)
  2. 调度随机(意味着数据访问会出现竞争)
  3. 线程间缺乏必要的同步机制
  4. 以上 3 点中,前两点不能改变,欲提高效率,传递数据,资源必须共享。只要共享资源,就一定会出现竞争。 只要存在竞争关系,数据就很容易出现混乱。
  5. 所以只能从第三点着手解决。使多个线程在访问共享资源的时候,出现互斥。

互斥量 mutex

Linux 中提供一把互斥锁 mutex(也称之为互斥量) 。
每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。

  1. 资源还是共享的,线程间也还是竞争的,
  2. 但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。


但,应注意:同一时刻,只能有一个线程持有该锁。
当 A 线程对某个全局变量加锁访问,B 在访问前尝试加锁,拿不到锁,B 阻塞。C 线程不去加锁,而直接访问 该全局变量,依然能够访问,但会出现数据混乱。
所以,互斥锁实质上是操作系统提供的一把“建议锁”(又称“协同锁”),建议程序中有多线程访问共享资源 的时候使用该机制。但,并没有强制限定。
因此,即使有了 mutex,如果有线程不按规则来访问数据,依然会造成数据混乱。

主要应用函数:

  1. pthread_mutex_init 函数
  2. pthread_mutex_destroy 函数
  3. pthread_mutex_lock 函数
  4. pthread_mutex_trylock 函数
  5. pthread_mutex_unlock 函数
  6. 以上 5 个函数的返回值都是:成功返回 0, 失败返回错误号。
  7. pthread_mutex_t 类型,其本质是一个结构体。为简化理解,应用时可忽略其实现细节,简单当成整数看待。
  8. pthread_mutex_tmutex; 变量 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_tmuetx=PTHREAD_MUTEX_INITIALIZER;
  2. 动态初始化:局部变量应采用动态初始化。e.g. pthread_mutex_init(&mutex,NULL)

pthread_mutex_destroy 函数

销毁一个互斥锁

int  pthread_mutex_destroy(pthread_mutex_t   *mutex);

pthread_mutex_lock 函数

加锁成功。可理解为将 mutex–(或-1)

 int pthread_mutex_lock(pthread_mutex_t  *mutex);

pthread_mutex_unlock 函数

解锁成功。可理解为将 mutex++(或+1)

 int pthread_mutex_unlock(pthread_mutex_t    *mutex);

pthread_mutex_trylock 函数

尝试加锁

int  pthread_mutex_trylock(pthread_mutex_t   *mutex);

示例:子线程打印小写,主控线程打印大写
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>pthread_mutex_t mutex; //锁的定义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)
{pthread_t tid;srand(time(NULL));pthread_mutex_init(&mutex,NULL); //mutex == 1pthread_create(&tid,NULL,tfn,NULL);while(1){pthread_mutex_lock(&mutex);printf("HELLO");sleep(rand()%3);printf("WORLD\n");pthread_mutex_unlock(&mutex);   sleep(rand() % 3);}pthread_mutex_destroy(&mutex);}

  1. 定义全局互斥量,初始化 init(&m,NULL)互斥量,添加对应的 destry
  2. 两个线程 while 中,两次 printf 前后,分别加 lock 和 unlock
  3. 将 unlock 挪至第二个 sleep 后,发现交替现象很难出现。
    线程在操作完共享资源后本应该立即解锁,但修改后,线程抱着锁睡眠。睡醒解锁后又立即加锁,这两个 库函数本身不会阻塞。
    所以在这两行代码之间失去 cpu 的概率很小。因此,另外一个线程很难得到加锁的机会
  4. main 中加 flag=5 将 flg 在 while 中-- 这时,主线程输出 5 次后试图销毁锁,但子线程未将锁释放,无法 完成。
  5. main 中加 pthread_cancel()将子线程取消。
注意事项:在访问共享资源前加锁,访问结束后立即解锁。锁的“粒度”应越小越好。

死锁

  1. 线程试图对同一个互斥量 A 加锁两次。
  2. 线程 1 拥有 A 锁,请求获得 B 锁;线程 2 拥有 B 锁,请求获得 A 锁
  3. 当不能获得所有的锁时,放弃已经占有的锁

读写锁

与互斥量类似,但读写锁允许更高的并行性。其特性为:写独占,读共享。

读写锁状态

一把读写锁具备三种状态:

  1. 读模式下加锁状态 (读锁)
  2. 写模式下加锁状态 (写锁)
  3. 不加锁状态

读写锁特性:

  1. 读写锁是“写模式加锁”时, 解锁前,所有对该锁加锁的线程都会被阻塞。
  2. 读写锁是“读模式加锁”时, 如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。
  3. 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁 会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高
  4. 读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独 占模式锁住的。写独占、读共享
  5. 读写锁非常适合于对数据结构读的次数远大于写的情况。

主要应用函数:

  1. pthread_rwlock_init 函数
  2. pthread_rwlock_destroy 函数
  3. pthread_rwlock_rdlock 函数
  4. pthread_rwlock_wrlock 函数
  5. pthread_rwlock_tryrdlock 函数
  6. pthread_rwlock_trywrlock 函数
  7. pthread_rwlock_unlock 函数
  8. 以上 7 个函数的返回值都是:成功返回 0, 失败直接返回错误号。
  9. pthread_rwlock_t 类型 用于定义一个读写锁变量。
  10. pthread_rwlock_trwlock;

pthread_rwlock_init 函数

初始化一把读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrictrwlock,const pthread_rwlockattr_t *restrictattr);
参 2:attr 表读写锁属性,通常使用默认属性,传 NULL 即可。

pthread_rwlock_destroy 函数

销毁一把读写锁

int  pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
pthread_rwlock_rdlock 函数

以读方式请求读写锁。(常简称为:请求读锁)

int  pthread_rwlock_rdlock(pthread_rwlock_t  *rwlock);
pthread_rwlock_wrlock 函数

以写方式请求读写锁。(常简称为:请求写锁)

int  pthread_rwlock_wrlock(pthread_rwlock_t  *rwlock);
pthread_rwlock_unlock 函数

解锁

int  pthread_rwlock_unlock(pthread_rwlock_t      *rwlock);
pthread_rwlock_tryrdlock 函数

非阻塞以读方式请求读写锁(非阻塞请求读锁)

int  pthread_rwlock_tryrdlock(pthread_rwlock_t   *rwlock);
pthread_rwlock_trywrlock 函数

非阻塞以写方式请求读写锁(非阻塞请求写锁)

int  pthread_rwlock_trywrlock(pthread_rwlock_t       *rwlock);
同时有多个线程对同一全局数据读、写操作
#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);//三个写线程,5个读线程for(i = 0; i < 8; i++)pthread_join(tid[i],NULL);pthread_rwlock_destroy(&rwlock);   //释放读写锁return 0;
}

Linux系统编程----16(线程同步,互斥量 mutex,互斥锁的相关函数,死锁,读写锁)相关推荐

  1. 【Linux系统编程】线程同步与互斥:互斥锁

    为什么需要互斥锁? 在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源.这个过程有点类似于,公司部门里,我在使用着打印机打印东西的同时(还没有打印完),别人刚好也在此刻使用打印机打印东西, ...

  2. 【Linux系统编程】线程同步与互斥:读写锁

    读写锁基本原理 当有一个线程已经持有互斥锁时,互斥锁将所有试图进入临界区的线程都阻塞住.但是考虑一种情形,当前持有互斥锁的线程只是要读访问共享资源,而同时有其它几个线程也想读取这个共享资源,但是由于互 ...

  3. Linux系统编程:使用semaphore信号量和mutex互斥量实现多个生产者和消费者模型

    代码实现 如题,使用semaphore信号量和mutex互斥量实现多个生产者和消费者模型.本来是想只用信号量实现生产者消费者模型的,但是发现 只能在一个生产者和一个消费者之间,要在多个生产者和消费者模 ...

  4. 【Linux系统编程】线程同步与互斥:POSIX无名信号量

    信号量概述 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问. 编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 ...

  5. Linux系统开发9 线程同步

    [本文谢绝转载原文来自http://990487026.blog.51cto.com] <大纲> Linux系统编程8 线程同步多线程共享资源,不加锁,同步互斥演示多线程共享资源,加锁,同 ...

  6. linux 进程间读写锁,Linux系统编程—进程间同步

    我们知道,线程间同步有多种方式,比如:信号量.互斥量.读写锁,等等.那进程间如何实现同步呢?本文介绍两种方式:互斥量和文件锁. ##互斥量mutex 我们已经知道了互斥量可以用于在线程间同步,但实际上 ...

  7. Linux系统编程——多任务的同步与互斥

    现代操作系统基本都是多任务操作系统,即同时有大量可调度实体在运行.在多任务操作系统中,同时运行的多个任务可能: 都需要访问/使用同一种资源 多个任务之间有依赖关系,某个任务的运行依赖于另一个任务 这两 ...

  8. 【Linux系统编程】线程池

    00. 目录 文章目录 00. 目录 01. 线程池原理 02. 线程池应用实例 03. 线程池代码 04. 附录 01. 线程池原理 在传统服务器结构中,常是有一个总的监听线程监听有没有新的用户连接 ...

  9. linux系统编程:线程原语

    线程原语 线程概念 线程(thread),有时被称为轻量级进程(Lightweight Process,LWP).是程序运行流的最小单元.一个标准的线程由线程ID.当前指令指针(PC),寄存器集合和堆 ...

最新文章

  1. opencv 显示图片
  2. c#控件弹幕效果_C# Form 实现桌面弹幕
  3. linux嵌入式智能家居环境监测系统的设计,智能家居环境监测系统.doc
  4. sql 存储过程 盲注入_一次非常规 SQL 注入(informixsql)的利用过程
  5. MongoDB数据查询
  6. 把字母排序ASC表c语言,c语言ASCII码排序
  7. ANSYS 有限元分析 概述
  8. php在线翻译,PHP 在线翻译函数代码
  9. 工业控制计算机固态硬盘,工业级SSD接口全解析,懂了你才会选对工业级SSD
  10. 【Web前端】HTML—6.表单标签
  11. 所谓的光辉岁月,并不是后来闪耀的日子,而是无人问津时你对梦想的偏执。
  12. 隧道工地考勤人员定位体系,提高施工人员作业安全-新导智能
  13. 【读书笔记】十年涨薪30倍:财务职场透视.html.pdf
  14. 全球及中国射频器件市场规模格局与投资战略决策报告2022版
  15. 用c语言构造真值表,构造命题公式的真值表--biaobiao88
  16. 移动机器人技术(8) 麦克纳姆轮全向移动机器人
  17. 图形引擎实战:移动平台海飞丝系统-运动篇
  18. Kerberos安装及使用3(Kerberos基本管理实践)
  19. iptable_netfilter介绍以及简单代码分析
  20. 《Sony Vegas Pro 12标准教程》——2.2 使用Vegas采集视频

热门文章

  1. Python3.5-20190501-廖老师的
  2. npm git 遇到的问题解决笔记
  3. 常用matlab函数(不定时更新)
  4. 强肝保肝养肝4大食物
  5. Python进阶06 循环对象
  6. 使用Beautifulsoup爬取药智网数据
  7. windows 7资源管理器崩溃解决方法
  8. 如何在子网中访问上层网络的计算机文件夹
  9. BSP for good 3d engine
  10. commons-lang的FastDateFormat性能测试