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

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

LinuxCPP1408在线视频

LinuxCPP1408

下一节:LinuxCPP1409

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

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

这是刚才我那个任务队列的

完整程序代码

我们的Job这个结构体

被我们定义成简单的一个二维点

有一个构造函数

我们定义一个任务队列

都是指向二维点的指针

std::list〈Job*〉 job_queue

定义一个互斥体:

pthread_mutex_t job_queue_mutex

这是一个互斥对象

初始化它

实现我们的线程函数

ProcessJob()处理我们作业的工作

简单的两个cout输出

线程函数的具体代码

处理作业的时候

我们需要加锁

这是一个被多个线程所共享的资源

所以每一个线程在访问它的时候

必须得到那个互斥

要锁定互斥才能访问共享资源

那么在处理这个作业的时候

就必须按照这个模式来

得到这个任务队列 怎么得到

你必须锁定那个用户队列

你才能得到

锁定不了 你就得不到

所以一开始我们要调用

pthread_mutex_lock()

去锁定这个任务队列互斥

锁定了这个互斥

就说明就是这个当前线程

获得了对这个共享资源的访问权

我们访问它

当这个队列的非空的时候

我们摘取这个队列的队首元

然后弹出队首元

把它删掉 从队列里删掉

得到它之后

立即我们就可以解锁

pthread_mutex_unlock() 解锁它

然后我们处理我们的作业

这是处理作业的时候的操作

必须使用这个互斥

访问这个共享资源

当你向这个队列中

添加一项任务的时候

也需要加锁

依然是共享操作

所以当我们构造了一个新的作业之后

我们要调用pthread_mutex_lock()

锁定这个任务队列

然后才能把这个元素

插入到这个队列的队尾

我们可以在这里边做一个输出

然后pthread_mutex_unlock()

解除这个锁定

事实上 解除这个锁定的动作

应该在push_back()之后

就立即做

让我们这个临界区内部的代码

尽可能得短

看我们的主程序

我们构造8个线程

其中5个线程

在这儿做入队的工作

做EnqueueJob

后面三个线程去做DequeueJob

出队的工作

就是添加作业任务的有5个线程

处理作业任务的有3个线程

就是这个意思 刚好是不等的

我有意设成不等的

你能看出差别 8个线程

5个去做入队的工作

3个去做出队的工作

都做完之后

我们调用pthread_join()

等待它们结束

主程序很简单

没有资源共享 没有互斥

这个问题就不好解决

刚才不讲了吗

你如果资源是被进程占用的

且没有办法释放

那么在多线程编程的时候

就非常容易导致死锁的问题

那么怎么处理这个死锁问题呢

那么你就可以更改互斥类型

把这个互斥属性对象

把它的类型从普通锁

给改成递归锁和检错锁

就能够避免死锁的发生

第二个线程同步机制是信号量

我们现在就有一个问题啊

如何确保我们的任务队列中

有任务可以做

怎么保证这一点呢

如果这个队列里没有任务

线程就可能退出了

当所有的线程都退出了以后

新的任务来了

但是我们没有线程可以做它了

这个不成 对吧

必须保证这一点

怎么处理它呢

这个时候我们就可以使用

POSIX标准的信号量

此前我们讲进程编程的时候

我们谈到过进程信号量

那个实现上就比较复杂

我们现在用的这个东西呢

主要用于线程同步而不是进程同步

它的实现就比较简单

它是POSIX标准

所以叫POSIX标准信号量

它用于在多个线程之间进行同步操作

操作方法也比进程信号量简单

用sem_init()这个函数

来初始化这个信号量

传的第一个是进程信号量对象

第二个就是它的共享属性

第三个就是信号量的初始值 特简单

最重要的两个操作

P操作和V操作

使用sem_wait()等待这个信号量

你也可以使用sem_trywait()、sem_timewait()

来等待这个信号量

这就是P操作

sem_wait()就是普通的等待

拿不到它就会阻塞

sem_trywait()就尝试着等待

拿不到它就不阻塞 直接返回

sem_timewait()性质跟sem_wait()差不多

只不过它是有一个定时

有一个时间期限

过了时间期限 它也就返回了

相对应地 就是发布这个信号量

Post动作 叫sem_post()这个函数调用

就可以发布一个信号量

这个对应就是V操作

信号量用完了 不用了 你可以销毁

就调用sem_destroy() 销毁它

我们看作业队列的例子

我们可以对刚才的程序代码

做一个修正

我们定义一个控制作业数目的

一个信号量叫job_queue_count

它是sem_t型的信号量

POSIX标准信号量 是这个定义

当我们处理这个作业的时候

我们首先要保证队列中有事可做

我有作业才能做

所以我们调用sem_wait()

传job_queue_count这个信号量进去

等待这个信号量值非零

有作业我才做 没作业我不做

很典型

如果有作业 我锁定互斥

访问它 用完了

解开这个互斥

加锁 解锁这个动作还要做的

这个不能省略的

这是DequeueJob()

接下来是EnqueueJob()

每一次新作业入队

就要发布一个信号量sem_post()

发布这个信号量

作业队列里又多了一个新作业

所以我们就sem_post()

发布这个信号量

同样地

互斥这一部分还不能省略

仍然必须保留着

主函数 记得一开始要sem_init()

做这样一次调用

初始化这个信号量

最后用完了

要调用sem_destroy()

销毁这个信号量

其它代码都和原来的那个版本

是一致的了

最后一个线程同步机制是条件变量

条件变量主要用于同步线程

对共享数据对象的访问

通过使用pthread_cond_init()这个函数

来初始化一个条件变量

如果你不用了

就通过pthread_cond_destroy() 销毁它

条件变量有个什么好处呢

就是你可以以广播的方式

唤醒所有等待目标条件变量的线程

你可以跟它说

我这个条件变量

你所等待的这个条件 得到了满足

那么所有等待这个条件的那些线程

都可以被你唤醒

这个动作叫广播

你也可以只唤醒一个

就只发出这样一个信号

这个条件变量这个条件得到满足了

就用pthread_cond_signal()

发出这个信号 唤醒

这叫唤醒

你可以做这个事情

可以广播可以唤醒

如果一个线程需要

等待某个特定的条件得到满足

那么它就可以调用pthread_cond_wait()

这个函数等待那个条件

特别注意 因为这样条件变量

会被好多个线程所共享

所以必须使用互斥

来对这个条件变量进行保护

从而确保这个操作的原子性

这是条件变量使用的时候

需要注意到的问题

基于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文件

LinuxCPP1408笔记与讨论

也许你还感兴趣的课程:

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