linux学习:线程

目录

线程api

创建一条新线程

获取、设置线程的分离属性

获取、设置线程是否继承创建者的调度策略

获取、设置线程的调度策略

[获取、设置线程静态优先级 获取、设置线程动态优先级](#获取、设置线程静态优先级 获取、设置线程动态优先级)

获取、设置线程栈大小、警戒区大小

退出线程

接合指定线程

给指定线程发送一个取消请求

获取、设置线程的取消状态和取消类型

压栈、或弹栈线程的取消处理例程

例子

创建一条线程,他的执行例程,线程退出,并被主线程接合

产生一个包含"分离"状态的属性变量,用此变量产生线程,使该线程退出后不会变成僵尸

取消一条线程,及该线程如何正确地处理该取消请求


线程api

创建一条新线程

注意

  • 线程例程指的是:如果线程创建成功,那么该线程会立即去执行的函数
  • POSIX 线程库的所有 API 对返回值的处理原则都是一致的:成功返回 0,失败返 回错误码 errno
  • 线程属性如果为 NULL,则会创建一个标准属性的线程,线程的属性非常多

线程属性变量api

以上 API 都是针对线程属性操作的,所谓线程属性是类型为 pthread_attr_t 的 变量,设置一个线程的属性时,通过以上相关的函数接口,将需要的属性添加到该类型 变量里面,再通过 pthread_create( )的第二个参数来创建相应属性的线程

注意

  • 定义线程属性变量,并且使用 pthread_attr_init( )初始化。
  • 使用 pthread_attr_setXXX( )来设置相关的属性
  • 使用该线程属性变量创建相应的线程
  • 使用 pthread_attr_destroy( )销毁该线程属性变量

获取、设置线程的分离属性

一条线程如果是可接合的,意味着这条线程在退出时不会自动释放自身资源,而会成为 僵尸线程,同时意味着该线程的退出值可以被其他线程获取。因此,如果不需要某条线程的 退出值的话,那么最好将线程设置为分离状态,以保证该线程不会成为僵尸线程

获取、设置线程是否继承创建者的调度策略

当需要给一个线程设置调度方面的属性时,必须先将线程的 inheritsched 设置为 PTHREAD_EXPLICIT_SCHED

获取、设置线程的调度策略

注意

  • 当线程的调度策略为 SCHED_FIFO 时,其静态优先级(static priority)必须设 置为 1-99,这将意味着一旦这种线程处于就绪态时,他能立即抢占任何静态优先级为 0 的 普通线程。采用 SCHED_FIFO 调度策略的线程还遵循以下规则:
    • A) 当他处于就绪态时,就会被放入其所在优先级队列的队尾位置。
    • B) 当被更高优先级的线程抢占后,他会被放入其所在优先级队列的队头位置,当 所有优先级比他高的线程不再运行后,他就恢复运行。
    • C) 当他调用 sched_yield( )后,他会被放入其所在优先级队列的队尾的位置。 总的来讲,一个具有 SCHED_FIFO 调度策略的线程会一直运行直到发送 I/O 请求, 或者被更高优先级线程抢占,或者调用 sched_yield( )主动让出 CPU
  • 当线程的调度策略为 SCHED_RR 时,情况跟 SCHED_FIFO 是一样的,区别在于: 每一个 SHCED_RR 策略下的线程都将会被分配一个额度的时间片,当时间片耗光时,他 会被放入其所在优先级队列的队尾的位置。可以用 sched_rr_get_interval( )来获得时间 片的具体数值
  • 当线程的调度策略为 SCHED_OTHER 时,其静态优先级(static priority)必须 设置为 0。该调度策略是 Linux 系统调度的默认策略,处于 0 优先级别的这些线程按照所 谓的动态优先级被调度,而动态优先级起始于线程的 nice 值,且每当一个线程已处于就绪 态但被调度器调度无视时,其动态优先级会自动增加一个单位,这样能保证这些线程竞争 CPU 的公平性

获取、设置线程静态优先级 获取、设置线程动态优先级

注意

  • 静态优先级是一个定义如下的结构体

    复制代码
    struct sched_param
    {
        int sched_priority;
    };

    可见静态优先级就是一个只有一个整型数据的结构体,这个整型数值介于0到99之间, 0 级线程被称为非实时的普通线程,他们之间的调度凭借所谓的动态优先级来博弈。而 1-99 级线程被称为实时线程,他们之间的调度凭借他们不同级别的静态优先级和不同的调度策略 (如果他们的静态优先级一样的话)来博弈

  • 线程的静态优先级(static priority)之所以被称为"静态",是因为只要你不强行使用相关函数修改他,他不会随着线程的执行而发生改变,静态优先级决定了实时线程的基本调度次序,如果他们的静态优先级一样,那么调度策略再为调度器提供进一步的调度依据

  • 线程的动态优先级(dynamic prioriy)是非实时的普通线程独有的概念,之所以 被称为"动态",是因为它会随着线程的运行,根据线程的表现而发生改变,具体来讲是--- ---如果一条线程是"CPU 消耗型"的,比如视频解码算法,这类线程只要一运行就黏住 CPU 不放,这样的线程的动态优先级会被慢慢地降级,这符合我们的预期,因为这类线程不需要 很高的响应速度,你只要保证一定的执行时间片就可以了。相反,另一类线程被称为"IO 消耗型",比如编辑器,这类线程绝大部分的时间都在睡眠,调度器发现每次调度他他都毅 然决然地放弃了,将宝贵的 CPU 让给了其他线程,了不起是个大好人,因此会慢慢地提高 他的动态优先级,使得这类线程在同等的非实时普通线程中,有越来越高的响应速度,表现 出更好的交互性能,这也正是我们想要的结果

获取、设置线程栈大小、警戒区大小

注意

  • 线程栈是非常重要的资源,用以存放诸如函数形参、局部变量、线程切换现场寄存器数 据等等,一个多线程进程的栈空间,包含了所有线程各自的栈

退出线程

线程跟进程类似,在缺省的状态下退出之后,会变成僵尸线程,并且保留退出值。其他线程可以通过相关 API 接合该线程------使其资源被系统回收,如果愿意的话还可以顺便获取其退出值

接合指定线程

注意

  • 如果线程退出时没有退出值,那么 retval 可以指定为 NULL
  • pthread_join( )指定的线程如果尚在运行,那么他将会阻塞等待
  • pthread_tryjoin_np( )指定的线程如果尚在运行,那么他将会立即出错返回

给指定线程发送一个取消请求

另外,或许在某个时刻不能等某个线程"自然死亡",而需要勒令其马上结束,此时可 以给线程发送一个取消请求,让其中断执行而退出

而当线程收到一个取消请求时,他将会如何表现取决于两个东西:一是当前的取消状态, 二是当前的取消类型。线程的取消状态很简单------分别是 PTHREAD_CANCEL_ENABLE 和 PTHREAD_CANCEL_DISABLE,前者是缺省的,代表线程可以接受取消请求,后者代 表关闭取消请求,不对其响应。

而在线程接受取消请求的情况下,如何停下来又取决于两种不同的响应取消请求的策略 ------延时响应和立即响应,当采取延时策略时,线程并不会立即退出,而是要遇到所谓的"取 消点"之后,才退出。而"取消点",指的是一系列指定的函数。

获取、设置线程的取消状态和取消类型

压栈、或弹栈线程的取消处理例程

由于线程任何时刻都有可能持有诸如互斥锁、信号量等资源,一旦被取消很有可能导致别的线程出现死锁,因此如果一条线程的确可能被取消,那么在被取消之前必须使用以下 API 来为将来可能出现的取消请求注册"处理例程",让这些例程自动释放持有的资源

例子

创建一条线程,他的执行例程,线程退出,并被主线程接合

复制代码
1 #include <stdio.h>
2 #include <pthread.h>
3 // 线程函数
4 void *routine(void *arg)
5 {
6     char *s = (char *)arg; // 将创建线程传进来的参数转换为其原本类型
7     printf("argument: %s", s);
8
9     sleep(1); // 睡眠 1 秒钟后退出
10    pthread_exit("Bye-Bye!\n");// 退出传出的值
11 }
12
13 int main(int argc, char **argv)
14 {
15     // 创建线程,指定其执行例程为 routine( )并将字符串传递给他
16     pthread_t tid;
17     pthread_create(&tid, NULL, routine, (void *)"testing string\n");
18
19     // 阻塞等待指定线程退出,并获取其退出值
20     void *p;
21     pthread_join(tid, &p);
22     // 打印其退出值
23     printf("exit value: %s", (char *)p);
24
25     return 0;
26 }

产生一个包含"分离"状态的属性变量,用此变量产生线程,使该线程退出后不会变成僵尸

复制代码
1 #include <stdio.h>
2 #include <pthread.h>
3 // 线程函数
4 void *routine(void *arg)
5 {
6     pthread_exit(NULL); // 由于已分离,该线程退出后会自动释放资源
7 }
8
9 int main(int argc, char **argv)
10 {
11     // 初始化一个属性变量,并将分离属性加入该变量
12     pthread_attr_t attr;// 创建一个属性变量
13     pthread_attr_init(&attr);// 初始化线程属性变量 attr
14     pthread_attr_setdetachstate(&attr, 
15                PTHREAD_CREATE_DETACHED);// 赋值给属性变量
16
17     // 用该属性变量产生一条新线程
18     pthread_t tid;
19     pthread_create(&tid, &attr, routine, NULL);
20
21     // 主线程暂停,否则 return 语句会导致整个进程退出
22     pause();
23     return 0;
24 }

取消一条线程,及该线程如何正确地处理该取消请求

复制代码
1 #include <stdio.h>
2 #include <pthread.h>
3
4 pthread_mutex_t m;// 声明了一个互斥锁 m
5 // 线程退出的时候会触发的解锁互斥锁函数
6 void handler(void *arg)
7 {
8     pthread_mutex_unlock(&m); // 解锁
9 }
10 // 线程函数
11 void *routine(void *arg)
12 {
13     // 加锁前,将 handler函数压入线程取消处理例程的栈中,以防中途被取消
14     pthread_cleanup_push(handler, NULL);
15     pthread_mutex_lock(&m);//上锁
16
17     printf("[%u][%s]: abtained the mutex.\n", 
18                 (unsigned)pthread_self(), 
19                               __FUNCTION__);
20     sleep(10); // 在此线程睡眠期间如果收到取消请求,handler 将被执行
21
22     
23     pthread_mutex_unlock(&m);// 解锁
24     pthread_cleanup_pop(0);// 将 handler函数从栈中弹出,但不执行它
25
26     pthread_exit(NULL);// 退出
27 }
28
29 int main(int argc, char **argv)
30 {
31     pthread_mutex_init(&m, NULL);// 初始化了互斥锁 m
32
33     pthread_t tid;// 新线程
34     pthread_create(&tid, NULL, routine, NULL);// 线程执行函数
35
36     // 等待 1 秒钟之后,向子线程发送一个取消请求
37     sleep(1);
38     pthread_cancel(tid);
39
40     // 此时子线程虽被取消了,但被 handler 自动释放,因此主线程可加锁
41     pthread_mutex_lock(&m);
42     printf("[%u][%s]: abtained the mutex.\n", 
43             (unsigned)pthread_self(), 
44             __FUNCTION__);
45     pthread_mutex_unlock(&m);// 解锁

46
47     return 0;
48 }
相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习
im_AMBER4 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J4 天前
从“Hello World“ 开始 C++
c语言·c++·学习