当前课程知识点:基于Linux的C++ >  第十四讲 线程编程 >  14.7 线程同步机制(一) >  LinuxCPP1407

返回《基于Linux的C++》慕课在线视频课程列表

LinuxCPP1407在线视频

LinuxCPP1407

下一节:LinuxCPP1408

返回《基于Linux的C++》慕课在线视频列表

LinuxCPP1407课程教案、知识点、字幕

在进行多线程编程的时候

因为资源竞争的原因

多个线程必须进行精确的同步

我们可以使用互斥 信号量或条件变量

对线程进行同步

我们首先来看这样的编程任务

假设我们存在一个任务队列

多个并发线程会同时处理这些任务

每一个线程在完成

它自己的当前那个任务之后

会检查这个任务队列里边

有没有新的任务

如果有 它就会把这个任务

从任务队列中取出来

然后去执行这个任务

这是我们这个线程要做的事

这个任务被某个线程

从任务队列中取出来了

那么这个任务

一定要从线程队列里面把它清除掉

现在我们假设

有两个线程碰巧都完成了各自的任务

任务队列里边呢

只有一个新任务还没有处理

可能发生的情况

第一个 线程发现这个任务队列里非空

然后它就要接收这个新的任务

但是它没有来得及完成全部的设置

这个时候操作系统碰巧中断了这个线程

启动了另外一个线程

另外一个线程也发现这个任务队列非空

然后它同样准备接受这个新任务

发现没办法设置了 处理不了了

最坏的情况

就是第一个线程已经从任务队列里边

把这个任务摘取出来了

但是还没有把那个任务队列清空

第二个线程

想去那个任务队列里边取数据

就会导致段错误 系统崩溃

这就是资源竞争条件下

多个线程运行的时候

要特别注意的问题

具体的代码是这个样子

假设我有一个列表

它是一个双向链表

使用一个标准模板库的

std::list这个类模板

作业嘛

我们随便定义一个结构体Job

链表里存的是指向这个job的指针

链表的名字叫job_queue

我们的任务队列

线程函数是DequeueJob()

把这个job从这个任务队列里边摘出来

出对 就这个意思

当这个任务队列非空的时候

我们就取这个任务队列的队首元

用job_queue.front()取到它的队首元

就是队列的顶头那个元素

叫队首 队首元

取到它 然后我们job_queue.pop_front()

弹出它

从这个任务队列里边

把这个队首元给删除掉

我们处理这个作业

这个任务处理完

我们销毁这个任务 Over

这就是DequeueJob()

但是当这个任务队列里边

只有一个任务

两个线程去竞争访问的时候

这段程序代码就会出问题

单线程的程序 这一切都很正常

但是多线程编程的时候

就需要特别注意 这就有问题

我们怎么解决它

第一种解决方案——互斥

什么叫互斥

互斥就是相互独占锁

它和二元信号量是非常非常类似的

一次只有一个线程可以锁定

一个数据对象并且访问它

其它线程必须等待

你等我用完了你才能用

这就叫相互独占

你可以独占 我也可以独占

但是不管怎么样

在任何时候只能有一个线程访问它

其它线程必须等待

这个就叫互斥

我们可以使用函数pthread_mutex_init()

这个函数 来初始化一个互斥对象

那个互斥对象的那个型

就叫pthread_mutex_t 初始化它

可以使用一个特定的互斥属性初始化它

有一个宏叫PTHREAD_MUTEX_INITIALIZER

可以完成互斥体的初始化

我们可以使用pthread_mutex_destroy()

销毁这个互斥

可以使用pthread_mutex_lock()

去锁定这个互斥

获取这个互斥的访问

pthread_mutex_lock()这个函数去锁定它

如果没有办法锁定的话

当前的线程就会被阻塞

一直到它能够成功地锁定

trylock 就tpthread_mutex_trylock()这个函数呢

它会尝试着去锁定它

当它锁定不成功的时候

这个函数就会被返回

它就不会阻塞这个当前线程

你用完了 你不就不用了嘛

那么这个锁你就得释放

你就得把这个锁给解锁

刚才是加锁 现在你就得解锁

解锁的话就使用

tpthread_mutex_unlock()解锁它

这几个函数都比较简单

但使用的时候都是需要特别注意的

一般来讲

你使用互斥的时候有一个固定的流程

首先你要定义pthread_mutex_t

这个型的一个对象

然后尝试着去锁定或者锁定这个互斥

获得访问权以后就做你特定的处理

处理完以后要解锁

设定这个互斥的时候

可以指定互斥体的属性

一般情况下边

我们可以将这个互斥属性设置为

进程共享的或进程私有的

什么意思呢

就是说这个互斥可以跨进程的共享

或者只能在本进程内共享

也就是本进程内所有的线程都共享它

但是不是本进程的线程

是不可以访问它的

就这个意思

大部分的时候

我们使用都是PTHREAD_PROCESS_PRIVATE

就是进程私有的

本进程内共享 进程外不共享

有的操作系统

就不支持进程间共享互斥

就只能是进程内共享

大部分操作系统都可以支持

但是很多时候

因为我们做的是多线程编程

往往是一个进程的很多个线程

所以进程内共享是最常见的属性

互斥锁 它实际上是有些特定的型的

在设定互斥属性的时候

我们可以设定它的type

也就是它的型

有几种典型的互斥锁

一个是PTHREAD_MUTEX_NORMAL

它是一种普通的锁 普通互斥

第二种就PTHREAD_MUTEX_ERRORCHECK

它是一种检错锁

它是做错误检查的

这个线程对已经被其它线程锁定的

互斥加锁的时候

它就会返回一个错误

它就能够检测出这个错误来

第三个就PTHREAD_MUTEX_RECURSIVE

它是一个递归锁

就允许一个线程对一个互斥

进行多次的加锁或解锁的动作

你加锁几次

当然你要解锁几次 是对应的

第四个就PTHREAD_MUTEX_DEFAULT

它就是一个缺省的一个锁

实现是上面三种中的某一个

它并不是单独的一个选择

缺省的时候

大部分系统使用都是第一个

PTHREAD_MUTEX_NORMAL

它是一个普通锁

我们用的最多的就是它

但是它会有一个主要的问题

当这样一个锁被某一个线程锁定以后

其它请求加锁的线程都会被阻塞

它将等待 特别注意这一点

这是一个非常简单的实现

它就有可能在某些特殊的情况下边

会导致死锁

这是需要特别注意的

当解锁被其它线程锁定

或者已经解锁的互斥的时候

这个结果可能是无定义的

会导致不可预计的结果

所以要特别注意这一点

有一系列的可以设定 获取 修改

互斥属性的函数

像pthread_mutexattr_init()

这是初始化互斥体属性的

pthread_mutexattr_destroy()

这是销毁互斥属性对象的

还有获取pshared属性

设置它的pshared属性

获取它的type属性

设置它的type属性的

等等等等 这些函数

在设置互斥属性的时候你都可以用

基于Linux的C++课程列表:

第一讲 C/C++基本语法元素

-1.1 提纲

--LinuxCPP0101

-1.2 程序设计的基本概念

--LinuxCPP0102

-1.3 简单C/C++程序介绍

--LinuxCPP0103

-1.4 程序设计的基本流程

--LinuxCPP0104

-1.5 基本语法元素

--LinuxCPP0105

-1.6 程序设计风格

--LinuxCPP0106

-1.7 编程实践

--LinuxCPP0107

-第一讲 C/C++基本语法元素--编程实践提交入口

第二讲 程序控制结构

-2.1 提纲

--LinuxCPP0201

-2.2 结构化程序设计基础

--LinuxCPP0202

-2.3 布尔数据

--LinuxCPP0203

-2.4 分支结构

--LinuxCPP0204

-2.5 break语句

--LinuxCPP0205

-2.6 循环结构

--LinuxCPP0206

-2.7 编程实践

--LinuxCPP0207

-第二讲 程序控制结构--编程实践提交入口

第三讲 函数

-3.1 提纲

--LinuxCPP0301

-3.2 函数声明、调用与定义

--LinuxCPP0302

-3.3 函数调用栈框架

--LinuxCPP0303

-3.4 编程实践

--LinuxCPP0304

-第三讲 函数--编程实践提交入口

第四讲 算法

-4.1 提纲

--LinuxCPP0401

-4.2 算法概念与特征

--LinuxCPP0402

-4.3 算法描述

--LinuxCPP0403

-4.4 算法设计与实现

--LinuxCPP0404

-4.5 递归算法(一)

--LinuxCPP0405

-4.6 递归算法(二)

--LinuxCPP0406

-4.7 容错与计算复杂度

--LinuxCPP0407

-4.8 编程实践

--LinuxCPP0408

-第四讲 算法--编程实践提交入口

第五讲 程序组织与开发方法

-5.1 提纲

--LinuxCPP0501

-5.2 库与接口

--LinuxCPP0502

-5.3 随机数库(一)

--LinuxCPP0503

-5.4 随机数库(二)

--LinuxCPP0504

-5.5 作用域与生存期

--LinuxCPP0505

-5.6 典型软件开发流程(一)

--LinuxCPP0506

-5.7 典型软件开发流程(二)

--LinuxCPP0507

-5.8 编程实践

--LinuxCPP0508

-第五讲 程序组织与开发方法--编程实践提交入口

第六讲 复合数据类型

-6.1 提纲

--LinuxCPP0601

-6.2 字符

--LinuxCPP0602

-6.3 数组(一)

--LinuxCPP0603

-6.4 数组(二)

--LinuxCPP0604

-6.5 结构体

--LinuxCPP0605

-6.6 编程实践

--LinuxCPP0606

-第六讲 复合数据类型--编程实践提交入口

第七讲 指针与引用

-7.1 提纲

--LinuxCPP0701

-7.2 指针基本概念

--LinuxCPP0702

-7.3 指针与函数

--LinuxCPP0703

-7.4 指针与复合数据类型(一)

--LinuxCPP0704

-7.5 指针与复合数据类型(二)

--LinuxCPP0705

-7.6 字符串

--LinuxCPP0706

-7.7 动态存储管理(一)

--LinuxCPP0707

-7.8 动态存储管理(二)

--LinuxCPP0708

-7.9 引用

--LinuxCPP0709

-7.10 编程实践

--LinuxCPP0710

-第七讲 指针与引用--编程实践提交入口

第八讲 链表与程序抽象

-8.1 提纲

--LinuxCPP0801

-8.2 数据抽象(一)

--LinuxCPP0802

-8.3 数据抽象(二)

--LinuxCPP0803

-8.4 链表(一)

--LinuxCPP0804

-8.5 链表(二)

--LinuxCPP0805

-8.6 链表(三)

--LinuxCPP0806

-8.7 链表(四)

--LinuxCPP0807

-8.8 函数指针(一)

--LinuxCPP0808

-8.9 函数指针(二)

--LinuxCPP0809

-8.10 抽象链表(一)

--LinuxCPP0810

-8.11 抽象链表(二)

--LinuxCPP0811

-8.12 编程实践

--LinuxCPP0812

-第八讲 链表与程序抽象--编程实践提交入口

第九讲 类与对象

-9.1 提纲

--LinuxCPP0901

-9.2 程序抽象与面向对象

--LinuxCPP0902

-9.3 类类型

--LinuxCPP0903

-9.4 对象(一)

--LinuxCPP0904

-9.5 对象(二)

--LinuxCPP0905

-9.6 类与对象的成员(一)

--LinuxCPP0906

-9.7 类与对象的成员(二)

--LinuxCPP0907

-9.8 类与对象的成员(三)

--LinuxCPP0908

-9.9 继承(一)

--LinuxCPP0909

-9.10 继承(二)

--LinuxCPP0910

-9.11 继承(三)

--LinuxCPP0911

-9.12 多态(一)

--LinuxCPP0912

-9.13 多态(二)

--LinuxCPP0913

-9.14 编程实践

--LinuxCPP0914

-第九讲 类与对象--编程实践提交入口

第十讲 操作符重载

-10.1 提纲

--LinuxCPP1001

-10.2 四则运算符重载(一)

--LinuxCPP1002

-10.3 四则运算符重载(二)

--LinuxCPP1003

-10.4 关系与下标操作符重载

--LinuxCPP1004

-10.5 赋值操作符重载(一)

--LinuxCPP1005

-10.6 赋值操作符重载(二)

--LinuxCPP1006

-10.7 赋值操作符重载(三)

--LinuxCPP1007

-10.8 赋值操作符重载(四)

--LinuxCPP1008

-10.9 赋值操作符重载(五)

--LinuxCPP1009

-10.10 流操作符重载(一)

--LinuxCPP1010

-10.11 流操作符重载(二)

--LinuxCPP1011

-10.12 流操作符重载(三)

--LinuxCPP1012

-10.13 操作符重载总结

--LinuxCPP1013

-10.14 编程实践

--LinuxCPP1014

-第十讲 操作符重载--编程实践提交入口

第十一讲 泛型编程

-11.1 提纲

--LinuxCPP1101

-11.2 泛型编程概览

--LinuxCPP1102

-11.3 异常处理机制(一)

--LinuxCPP1103

-11.4 异常处理机制(二)

--LinuxCPP1104

-11.5 运行期型式信息(一)

--LinuxCPP1105

-11.6 运行期型式信息(二)

--LinuxCPP1106

-11.7 模板与型式参数化

--LinuxCPP1107

-11.8 题外话:术语翻译

--LinuxCPP1108

-11.9 泛型编程实践(一)

--LinuxCPP1109

-11.10 泛型编程实践(二)

--LinuxCPP1110

-11.11 泛型编程实践(三)

--LinuxCPP1111

-11.12 泛型编程实践(四)

--LinuxCPP1112

-11.13 泛型编程实践(五)

--LinuxCPP1113

-11.14 泛型编程实践(六)

--LinuxCPP1114

-11.15 泛型编程实践(七)

--LinuxCPP1115

-11.16 泛型编程实践(八)

--LinuxCPP1116

-11.17 泛型编程实践(九)

--LinuxCPP1117

-11.18 泛型编程实践(十)

--LinuxCPP1118

-11.19 编程实践

--LinuxCPP1119

-第十一讲 泛型编程--编程实践提交入口

第十二讲 Linux系统编程基础

-12.1 提纲

--LinuxCPP1201

-12.2 程序执行环境(一)

--LinuxCPP1202

-12.3 程序执行环境(二)

--LinuxCPP1203

-12.4 程序执行环境(三)

--LinuxCPP1204

-12.5 程序执行环境(四)

--LinuxCPP1205

-12.6 输入输出(一)

--LinuxCPP1206

-12.7 输入输出(二)

--LinuxCPP1207

-12.8 文件系统

--LinuxCPP1208

-12.9 设备

--LinuxCPP1209

-12.10 库(一)

--LinuxCPP1210

-12.11 库(二)

--LinuxCPP1211

-12.12 makefile文件(一)

--LinuxCPP1212

-12.13 makefile文件(二)

--LinuxCPP1213

-12.14 makefile文件(三)

--LinuxCPP1214

-12.15 编程实践

--LinuxCPP1215

-第十二讲 Linux系统编程基础--编程实践提交入口

第十三讲 进程编程

-13.01 提纲

--LinuxCPP1301

-13.02 进程基本概念

--LinuxCPP1302

-13.03 信号

--LinuxCPP1303

-13.04 进程管理(一)

--LinuxCPP1304

-13.05 进程管理(二)

--LinuxCPP1305

-13.06 进程管理(三)

--LinuxCPP1306

-13.07 进程间通信(一)

--LinuxCPP1307

-13.08 进程间通信(二)

--LinuxCPP1308

-13.09 进程间通信(三)

--LinuxCPP1309

-13.10 进程间通信(四)

--LinuxCPP1310

-13.11 进程池

--LinuxCPP1311

-13.12 编程实践

--LinuxCPP1312

-第十三讲 进程编程--编程实践提交入口

第十四讲 线程编程

-14.1 提纲

--LinuxCPP1401

-14.2 线程基本概念

--LinuxCPP1402

-14.3 线程管理(一)

--LinuxCPP1403

-14.4 线程管理(二)

--LinuxCPP1404

-14.5 线程管理(三)

--LinuxCPP1405

-14.6 线程管理(四)

--LinuxCPP1406

-14.7 线程同步机制(一)

--LinuxCPP1407

-14.8 线程同步机制(二)

--LinuxCPP1408

-14.9 C++11线程库(一)

--LinuxCPP1409

-14.10 C++11线程库(二)

--LinuxCPP1410

-14.11 C++11线程库(三)

--LinuxCPP1411

-14.12 C++11线程库(四)

--LinuxCPP1412

-14.13 C++11线程库(五)

--LinuxCPP1413

-14.14 编程实践

--LinuxCPP1414

-第十四讲 线程编程--编程实践提交入口

第十五讲 网络编程

-15.1 提纲

--LinuxCPP1501

-15.2 Internet网络协议

--LinuxCPP1502

-15.3 套接字(一)

--LinuxCPP1503

-15.4 套接字(二)

--LinuxCPP1504

-15.5 编程实践

--LinuxCPP1505

-第十五讲 网络编程--编程实践提交入口

课程文档

-课程PDF文件

LinuxCPP1407笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。