c++ 获取线程id_UNIX环境高级编程(APUE)学习之路第11章-11.2 线程的创建、取消和终止、栈的清理...

d3b9d18c54efcee43814c1c0b651802a.png

线程创建

#include 

参数解析: tidp:当pthread_create成功返回后,新创建线程的线程ID会被设置成tidp指向的内存单元。

attr:用于定制不同的线程属性(在第12章会详细讨论),将其设置为NULL将会拥有默认的属性

start_rtn:新创建的线程开始的地址,其实就是兄弟线程。这里其实暴露了最好的结构,因为这是一个返回值为任意类型,参数为任意类型的指针函数。因此非常开放,可以是任何类型。

arg:结构体的地址

注意:

pthread函数在调用失败后通常会返回错误码。在线程中,从函数中返回错误码更为清晰整洁,不需要依赖那些随着函数执行不断变化的全局状态,这样可以把错误的范围限制在引起出错的函数中。

举例(书中)

#include 

输出:

6b43628fca55b63c9c420f7d0c993caa.png

解释: 在这里,main进程先是创建了一个新的线程,其中线程id是ntid属性默认,兄弟线程设置为thr_fn,其余参数设置为NULL。 最终输出创建出来的线程的各种参数和main线程的各种参数。

举例2

#include 

解析:

在这里,可以看到首先输出Begin,然后创建一个线程,最后输出Hello world。 创建线程的时候会执行兄弟线程。

注意: 因为兄弟线程和main线程的执行线程是不确定的,因此如下图所示,兄弟线程的语句什么时候输出以及是否输出都是不确定的。

05cfb2d703223373547a24b90982e986.png

线程的终止

3种终止线程的方式

  1. 线程可以简单地从启动例程中返回,返回值是线程的退出码
  2. 线程可以被同一进程中的其他线程取消
  3. 线程调用pthread_exit
#include 

参数: rval_ptr参数是一个无类型指针,与传给启动例程的单个参数类似。进程中的其他线程也可以通过调用pthread_join函数访问到这个指针。

#include 

参数: 其中pthread_exit的参数和pthread_join函数的第二个参数是相同的,pthread_join函数的含义是将调用点成阻塞,直到指定的线程(也就是thread参数指定的)从它的启动例程返回,rval_ptr包含返回码。如果线程被取消,由rval_ptr指定的内存单元就被设置为PTHREAD_CANCELED。

pthread_create和pthread_join线程举例

#include 

输出:

1361501f5af5f54509c0a9f8efeba19f.png

解释: main线程首先创建了一个子线程,线程号被指向tid,属性是默认属性,兄弟线程是func。创建线程之后用pthread_join来等待这个线程退出,最后执行End!

注意:若没有pthread_join则输出顺序是不确定的。就和例1一样。

我的例子

#include 

可以看到,我们首先创建了一个pthread_t类型的变量tid,初始值是-407197560.然后打印Begin,调用pthread_create来创建一个线程,指向tid,属性为NULL,兄弟线程是func,可以看到,线程号在调用期间被初始化成了141393920.然后结束之后打印End。

例11.3(获取已终止线程的退出码)

#include 

输出:

6ea77130654ef7b8f9d3e98d35461529.png

不要传递临时变量

pthread_create和pthread_exit函数的无类型指针参数可以传递的值可以是结构体等一系列类型,但是这个结构所使用的内存在调用之后必须仍然是有效的。也就是说,不要是临时变量(存放在栈上),需要是存放在堆上的常量或者其他数据。参考指针函数和函数指针。里面有详细的介绍。

用自动变量(分配在栈上)作为pthread_exit的参数时出现的问题。

#include 

输出:

2d0ad5c86f6e7fbadca3cf7f2df9200e.png

注意:这是macos上的输出,每个操作系统的输出不一定相同。 在这里,在第41行调用tid1线程时将fp进行了初始化,但是调用结束之后内存就回收了,因此被覆盖了。在第50行再次被调用时已经没有办法显示原来的值了。

线程的取消

函数:pthread_cancel();

#include 

在默认情况下,pthread_cancel函数会使得tid表示的线程的行为表现为如同调用了PTHREAD_CANCELED和pthread_exit函数,但是,线程可以选择忽略取消或者控制如何被取消。 注意:pthread_cancel并不等于线程终止,它仅仅提出请求。

取消两种状态

1.允许

允许取消也分为两种:

① 异步cancel,推迟cancel(默认)->推迟至cancel点在响应,也就是说不到cancel点是不会直接取消的。 cancel点:就是允许cancel的位置

② 同步cancel

2.不允许

线程清理处理程序

线程可以安排它推出时需要调用的函数,这与进程在退出时可以用atexit函数安排退出是类似的。这样的函数称为线程清理处理程序。

一个线程可以建立多个线程处理程序,处理程序记录在栈中,也就是说,它们的执行顺序与它们注册时相反。

#include 

举例

#include 

输出:

f5dd3770e12c6b22403d213f2ff0ee01.png

可以看到,退出时会依次执行三个退出时我们所需要调用的函数。并且执行顺序与注册时相反。

注意

来看两种特殊情况, 1. 我们将弹出栈pop的三行设置如下:

pthread_cleanup_pop

此时会只有一个输出,如下:

eee1840167b23fbae2c458fd2dae37ec.png

可以看到只有最后一次push的值输出的。其余的都没有输出

2. 若我们将上面的三行放到pthread_exit(NULL)之后,那么理论上是看不到的,但是因为线程没办法看到,所以pop值全部默认为1!! !

0856d576beb2405dc22f14c40829ba0c.png

此时的输出为:

71a580ab6419aff8f7b1364fc515883b.png

并且即使看不见,也必须在程序里体现,因为在宏定义中push里有一个{,而}在pop中,因此必须得成对出现,数量相同。

cancel点

在POSIX标准中,只有在cancel点才会执行阻塞系统调用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值