关于线程的ID有两个函数你可以用
第一个是pthread_equal()
判定两个线程是不是相等
第二个是pthread_self()
返回当前线程的ID
有的时候我们对当前线程的ID
会特别的关注
因为我们要区分我一个程序中
启动的很多个线程
它到底哪一个是哪一个 对吧
所以这两个函数你都可以用
在创建线程的时候
可以通过设置线程的属性
来精确地调整线程的行为
设置线程的属性过程
有一个固定的流程
和以前我们讲进程间通讯的时候
它的很多属性的设置都非常得类似
首先你要创建一个pthread_attr_t
这种型的一个对象
然后你调用pthread_attr_init()这样的函数
来初始化线程属性对象的一个缺省设置
做完了这个之后
你就可以精细地
去调整它的属性中的每一个选项了
设定完线程的属性
你再调用pthread_create()
创建这个线程的时候
就可以传递
这个线程属性对象的地址进去
它就会使用你设定好的
这个线程属性去创建线程
如果在线程创建完了之后
这个线程属性对象你不要了
那你就可以调用pthread_attr_destroy()
销毁它
销毁这个动作仅仅是销毁
线程属性对象里边的属性设置
它并没有清除这个对象本身
所以你可以重新地调用
pthread_attr_init()去初始化它
再次初始化 再次使用它
这个是没有关系的
有一点是需要说明的
单一的一个线程属性对象
可以用来创建很多个线程
这个没关系
对它来说 它只是一个线程属性对象
它只需要从中获取线程创建时候的
一些特定属性值
能够得到这个值就行
所以从某种程度来讲一个
单一的一个线程属性对象
可以构造很多很多个线程 这是一个
第二个 线程创建以后
继续保留这个线程属性对象本身
是没有意义的
所以大部分时候
我们都会destroy它 销毁它
对于大多数Linux程序来说
线程属性对象中最重要的那个属性
就是它的分离状态
其它我们很少会设定
一个Linux线程
从分类的角度来讲 是分成两大类
一类是可联线程
一类我们称之为分离线程
什么是可联线程呢
就是这样的一个线程在中止的时候
并不去做自动地清除工作
这个时候就会导致这样的线程
就像僵尸进程一样结束了
但是它的状态并没有被取出来
主线程必须调用join那个函数
来等待它结束
才能去完成它最后的清除工作
这个就叫joinable
所以叫可联线程 你要联接它
然后才能做最后的清除工作
第二种就是你不需要去做清除工作的
那么这就叫分离线程
你把这个线程设置成一个分离的
那么这个线程做完了
它自动地就被清除掉了
就什么你不用管了
主线程就不用管它了
就这个意思
缺省设置是可联线程
主线程必须调用pthread_join()
去等待它结束
这是很典型的线程的分类
就这么两种
一个可联线程可以通过pthread_detach()
这样的一个函数调用把它分离
但是你分离过了以后
这个线程就不可以再次联接了
你再想把它联接进来那就不行了
detach这线程一旦被分离了
像这样的线程一旦调用了pthread_detach()
这样的函数分离了它
那么你就再也不能调用pthread_join()
去等待它了
这一点是需要特别注意的
所有的线程缺省的设置都是可联线程
你都必须调用pthread_join()
去等待它结束了
有两个函数可以用来设置或获取
线程的分离状态
一个是pthread_attr_setdetachstate()
这是设置线程的分离属性
还有一个函数叫pthread_attr_getdetachstate()
这个就是获取线程的分离属性
我们最关心的是那个set函数
你传一个线程属性对象的一个指针
然后你传一个
它的分离状态参数进去 就OK了
缺省情况下它不是一个可联线程吗
所以如果你在创建线程的最初
就把它变成一个分离线程
那么你的第二个参数你就传
PTHREAD_CREATE_DETACHED
就是创建的时候
就让这个线程处于分离状态
以后你就再不用等待结束了
它自己结束 它就over
就调用这个函数就OK了
我们看这样一个例子
在主函数里
我们会定义一个线程属性对象attr
然后呢调用pthread_attr_init()初始化它
完成初始化以后我们调用
pthread_attr_setdetachstate()
传分离线程标志
把这个属性给它传进去 设定好它
然后你就可以调用pthread_create()
去创建这个线程
然后这个线程就会运行
完事以后
这个属性对象 我们就调用
pthread_attr_destroy()销毁
就完了 很简单吧
就是一个基本属性的设置
你完成它的设置 就OK了
只不过这里边有一系列的属性
每个可能需要做的设置
你都需要查阅帮助手册
然后才能知道我为什么要做这个设置
如果我需要某种特定的设置
我应该设置什么样的值
我们的课堂上
不可能事无巨细 面面俱到 对吧
所以这些地方你知道怎么用就行了
具体的细节在需要的时候查阅帮助手册
有一个函数叫pthread_cancel()
用来负责撤销一个线程
这个函数特别简单
就传一个线程ID进去就完了
已撤销的线程可以联接
实际上 我们也必须联接这个线程
然后释放它的资源
你不联接的话
它的好多最后的清除工作就没法做
这个线程被撤销了
那么你就联接它 释放它的资源
除非 它是一个分离线程
那你就不能管了
这是线程的撤销
线程的撤销 它有自己的类型或状态
就三种 一个是异步可撤销
就在它线程执行的任何时候
你都可以把它撤销掉
这叫异步可撤销
第二种就是同步可撤销
就是线程你可以撤销
但是这个撤销呢
必须要等到一些特定的撤销点
你才能去做
所有的这些撤销操作形成一个队列之后
你要入队 去排队的
到了一个特定的撤销点
然后你才去做撤销的动作
所以它是一种延迟的撤销
还有一种当然就是不可撤销
这个线程本身是不能够被撤销的
所有对这个线程发出的撤销请求
它都会被系统自动地忽略
线程是不管的
可以通过这个函数
pthread_setcanceltype() 来设定
最重要的两个参数
就是PTHREAD_CANCEL_ASYNCHRONOUS
这是一个异步可撤销
还有PTHREAD_CANCEL_DEFERRED
defer叫延迟
延迟可撤销
其实就是同步可撤销
要到固定的撤销点它才会撤销
这就叫同步可撤销
用pthread_setcancelstate()
来设置线程的撤销状态
刚才做的是设置线程的撤销类型
现在我们使用pthread_setcancelstate()
来设置线程的撤销状态
这是决定这个线程能不能够被撤销
刚才是以什么样的方式撤销
这是能不能够撤销
所以PTHREAD_CANCEL_ENABLE
说明这个线程是可撤销的
就注意这一点
这个函数pthread_testcancel()
用于设置对应的撤销点
它如果设置成功了这个撤销点
到了这个撤销点
那么当发现这个线程是属于
延迟可撤销的 也就是同步可撤销的
那么它就会撤销那个线程
所以在编程的时候
要特别注意这一点 就是什么呢
就是当你有一些线程
需要以一个同步可撤的模式
进行撤销的时候
那么你就要周期性地
在你的函数内部调用这个函数
设定一些撤销点
这样的话能保证这个线程在完结了之后
尽快地会被撤销
做最后的清除工作
在编程的时候有一个技术细节
就是我们可以使用这个线程的撤销状态
来构造一个临界区
什么叫临界区呢
临界区就是一个关键性的一个区域
在这个关键性区域里边的代码
要么就全部执行 要么就一条都不做
那么你就可以设置
这个线程的撤销状态
这个线程一旦进入这个临界区
你就把它设置成一个不可撤销的这个模式
那么只能等它离开临界区
你恢复它一个可撤销模式之后
线程才能够被撤销
这样的话
这段代码就保证它一定会被执行
或者一定不被执行
这就完成了临界区的设计
我们看它的具体的示例
实际内容本身很简单
我们这里边想做一个transfer
想做一个转账
一个from这个账户转到to的这个账户
把一部分钱从一个from账户
转到to这个账户
当然这是一个账户的数组
from和to都是这个账户的
数组里边的下标
就从一个账户转到另外一个账户
我想转一笔钱过去
这笔钱转账嘛 因为一边账户要减
另外一边账户要加
所以这个动作应该要么都做
要么都不做
不能一边减了 那边没加
那不就完蛋了嘛 数据就不一致了
所以这个地方就需要特别注意
所以这个操作应该做成一个临界区
那么当我们构造这个线程的时候
就可以在进入这个临界区之前
调用pthread_setcancelstate()
说明我不允许你撤销我这个线程
接下来执行的代码
然后调用pthread_setcancelstate()
重新恢复它原来的线程状态
这样的话就保证我这段代码
要么就没做 要么就一定被做完了
这就是线程的撤销