当前课程知识点:基于Linux的C++ >  第十四讲 线程编程 >  14.6 线程管理(四) >  LinuxCPP1406

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

LinuxCPP1406在线视频

LinuxCPP1406

下一节:LinuxCPP1407

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

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

接下来一个技术细节

是线程的局部存储 叫TLS

我们用它

来存储每个线程的独有的数据

或者有时候也称线程的特定数据

前面不是讲过了吗

进程的多个线程是通过全局的堆

来共享全局数据对象的

一个进程 它在全局堆里

分配了一些特定的数据对象

那么这些数据对象

会被这个进程的所有的线程所共享

都能看得见 都能用

那么如果有几个线程

想要设定这个全局数据对象的值呢

那到底哪一个为准呢

所以很多时候每个单独的线程

都想访问自己的

那个全局对象的唯一的一个副本

那么怎么保证这一点呢

这就叫线程的局部存储

我们想让这个线程

拥有一个全局数据对象独立的副本

那么就必须使用线程局部存储技术

在这里那些全局数据对象

是不可以简单地赋值和读取的

那么我们怎么设置线程局部存储呢

有这样的线程局部存储管理函数

第一个pthread_key_create()

它会构造一个特定的键值

把它和线程局部存储的那个数据对象

关联起来

那么我们接下来就可以通过这个键值

对这个线程局部存储数据对象

进行控制和管理

两个函数pthread_setspecific()

和pthread_getspecific()就专门用来做这个

我们在这里定义一个静态的量

pthread_key_t tlk

这就是关联线程日志文件的

一个特定的键

当然关联的是那个文件指针

做的就是线程局部存储

我们这个例子要做什么事情呢

就是我们要启动一系列的线程

在这个线程里边

每一个线程会做一些特定的处理

并且把它处理的事务做个记录

也就是每个线程

都有自己独立的日志文件

你启动了很多个线程

这些线程共用一个日志文件

显然不太好

我要区分哪个线程是哪一个吗 对吧

单独使用线程日志文件显然更方便

数据不就不共享了嘛

但是这个日志文件本身作为一个对象

在我们的程序内部一开始

这是独一的

当你创建很多个线程的时候

显然不能访问那个独一的文件

因为每个特定的文件那个文件的名字

都应该和这个线程的ID是相关的

所以我们就需要使用线程局部存储

来处理这个问题

在我们的线程函数里

我们会构造跟这个线程相匹配的文件

文件名字怎么来的呢

就是看这个线程的ID

得到这个线程的ID

构造这个线程日志文件的名字

然后我们打开这个文件

就得到了那个文件指针

你注意 因为对于不同的线程来讲

哪怕使用的是同样一段代码

它访问的那个线程的日志文件

名字都是不一样的

跟它的ID相关嘛

怎么可能一样呢 肯定不一样

所以这个文件指针针对于不同的线程

应该指向不同的文件

这就是线程局部存储了

那么普通的访问手段就不成了

我们就必须使用线程局部存储的技术

我们要定义static pthread_key_t

这样型的一个对象 tlk

我们就用它这个特定的键值tlk

和每个线程的日志文件指针关联在一起

函数WriteToThreadLog()

这就是向线程日志文件里边写入数据

传的参数就是一个写入的消息

我们就通过pthread_getspecific()

这个函数的调用

通过tlk这个键值

得到它所保护的那个文件指针

然后向那个文件里写入数据

回过头来看线程函数

我们得到这个文件指针以后

我们一定要调用pthread_setspecific()这个函数

将这个文件指针和这个键值关联起来

一旦有了这个函数调用

那么这个线程局部存储

就被你构造出来了

每一个线程都可以使用tlk这个键值

来访问它自己所独有的

那个文件指针对象

我们这里面提供了几个线程

8个线程

所以每个线程都会有自己的

独有的那一个文件指针fp

它们都指向不同的地方

这就是线程的局部存储

特别需要注意的是

在我们的主函数里

你要想让你的pthread_getspecific()、

pthread_setspecific()能够工作

那么你必须要完成这个线程的创建

要在线程的创建之前

完成这个线程的局部存储键值的创建

所以要先调用pthread_key_create()

要创建这个线程的对应的键

创建的就是tlk

要传递一个清除这个

线程局部存储的那个属性对象的

那个函数

我们传的是CloseThreadLog

就关闭这个线程 要把它清除掉

用完了以后 记得pthread_key_delete()

销毁这个键值

这一小节最后一个知识点

就是线程的清除

线程的清除函数

它就是一个典型的回调函数

单void*参数 没有返回值

它的最主要的目的

就是销毁线程退出或被撤销时

没有来得及释放的资源

你写了这样的一个线程清除函数

然后你就可以调用pthread_cleanup_push()

把这个线程清除函数注册进去

当线程结束以后

你可以通过调用pthread_cleanup_pop()

取消这个线程的注册

你传递一个非零值

那么它就在取消注册的时候

替你完成线程最后的清除工作

你不pop 它实际上是不清除的

你一pop 传一个非零值

它实际上替你完成这个

线程的清除工作

你传0它实际上也不清除

在这里我有一个函数AllocateBuffer()

要分配一个缓冲区

然后有一个对应的清除函数

DeallocateBuffer()

要销毁这个缓冲区里面的数据

我们有一个函数DoSomeWork()

它要做一些特定的事务处理

那么在这里我们会分配一段内存

分配一段动态存储区

分配完了以后呢

你要保证这个资源能够被释放嘛

所以我们就注册一个线程清除函数

pthread_cleanup_push()

把这个DeallocateBuffer

这个函数给它注册进去

我们注册这个清除函数

然后当它执行完了以后

调用pthread_cleanup_pop()

传一个非零的值

它就会取消这个函数的注册

执行那个函数

把这个线程分配的这个缓冲区给销毁

简单吧 就是一个回调函数的实现

注册、弹出

就这两件事情 做完就行

在线程的清除的过程中

有一个非常重要的问题

可能用到了C++的部分特性

但是写的主要是C的代码呀

那么真要是用面向对象来实现呢

可能就会有一些问题

对象的析构函数在线程退出的时候

可能没有机会去获得执行

那么这个函数没做 没析构

那这个对象怎么办呢

这不就有一个问题吗

线程栈上的这个数据

就有可能没有被清除

那么你怎么才能够保证

这样的线程资源能够被正确地释放呢

有一个解决方案就是这样

你可以定义一个异常类

在线程 在它准备退出的时候

就会触发这个异常

然后你在异常处理里边

完成这个资源的释放

你引发异常的时候

C++会确保那些对象的析构函数

会被调用的

很不同寻常的解决方案

但是能够解决我们的问题

我们看代码

我们定义一个异常类EThreadExit

退出线程异常

实现非常简单

就是构造函数

一个简单的值 构造

有一个公有的成员函数DoThreadExit()

调用pthread_exit()

退出这个线程就完了

线程函数 try

当线程需要立即退出的时候

我们throw 抛出这个异常

然后我们catch 抓住这个异常

抓住这个异常以后做什么事情呢

就做我们线程退出 Over

干嘛要这么写

最重要的就是在这里

我们可以在DoThreadExit()这个函数内部

完成资源的清除工作

你也可以在catch这个子句里

完成这个线程的清除工作

这样的话 我们保证在任何时候

线程的里边所分配的资源

都会被正确地释放

从而能够完成全部资源的清除

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

LinuxCPP1406笔记与讨论

也许你还感兴趣的课程:

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