36.1 线程介绍
36.1.1 线程的基本概念
- 进程是资源管理的最小单位,线程是程序执行的最小单位
- 每个进程都有自己的数据段、代码段和堆栈段。
- 线程通常叫做轻型的进程,它包含独立的栈和 CPU 寄存器状态,线程是进程的一条执行路径,每个线程共享其所附属进程的所有资源,包括打开的文件、内存页面、信号标识及动态分配的内存等。
- 因为线程和进程比起来很小,所以相对来说,线程花费更少的 CPU 资源
- 在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器,并且减少进程上下文切换的开销。
36.1.2 进程和线程的关系
- 线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一用户内存空间,当进程退出时,该进程所产生的线程都会被强制退出并清除。
- 一个进程至少需要一个线程作为它的指令执行,进程管理着资源(比如 CPU、内存、文件等等),并将线程分配到某个 CPU 上执行
36.1.3 线程分类
- 线程按照其调度者可分为用户级线程和内核级线程两种:
- 用户级线程:主要解决的是上下文切换的问题,其调度过程由用户决定
- 内核级线程:由内核调度机制实现
- 现在大多数操作系统都采用用户级线程和内核级线程并存的方法
- 用户级线程要绑定内核级线程运行,一个进程中的内核级线程会分配到固定的时间片,用户级线程分配的时间片以内核级线程为准
- 默认情况下,用户级线程和内核级线程是一对一,也可以多对一,这样实时性就会比较差
- 当 CPU 分配给线程的时间片用完后但线程没有执行完毕,此时线程会从运行状态返回到就绪状态,将 CPU 让给其他线程使用
36.1.4 Linux 线程实现
- 以下线程均为用户级线程
- 在Linux 中,一般采用 pthread 线程库实现线程的访问与控制,由 POSIX 提出,具有良好的可移植性
- Linux 线程程序编译需要在 gcc 上链接库 pthread
36.1.5 线程标识
- 每个进程内部的不同线程都由自己的唯一标识(ID)
- 线程标识只在它所属的进程环境中有效
- 线程标识是 pthread_t 数据类型
1 #include <pthread.h> 2 int pthread_equal(pthread_t, pthread_t);
- 函数功能:判断两个线程是否相等
- 返回值:相等返回非0;否则返回0
1 #include <pthread.h> 2 pthread_t pthread_self(void);
- 函数功能:获取当前线程的线程 ID
- 返回值:调用线程的线程 ID
36.2 线程的创建和销毁
36.2.1 线程创建
1 #include <pthread.h> 2 int pthread_create(pthread_t *restrict tidp, 3 const pthread_attr_t *restrict attr, 4 void *(*start_rtn)(void *), 5 void *restrict arg);
- 函数功能:创建一个线程
- 函数参数:
- tidp:线程标识符指针
- attr:线程属性指针
- start_rtn:线程运行函数的起始地址
- arg:传递给线程运行函数的参数
- 返回值:成功,返回0;失败,返回错误编号
- 新创建线程从 start_trn 函数的地址开始运行
- 不能保证新线程和调用线程的执行顺序
36.2.2 线程终止
- 主动终止:
- 线程的执行函数中调用 return 语句
- 调用 pthread_exit()
- 被动终止:
- 线程可以被同一进程的其他线程取消,其他线程调用 pthread_cancel(pthid)
1 #include <pthread.h> 2 int pthread_cancel(pthread_t tid); 3 void pthread_exit(void *retval); 4 int pthread_join(pthread_t th, void **thread_return);
- pthread_cancel:线程可以别同一进程的其他线程取消,tid 为被终止的线程标识符
- pthread_exit:
- retval:pthread_exit 调用者线程的返回值,可由其他函数和 pthread_join 来检测获取
- 线程退出时,使用函数 pthread_exit,是线程的主动行为
- 由于一个进程中的多个线程共享数据段,因此通常在线程退出后,退出线程所占用的资源并不会随线程结束而释放。所以需要 pthread_join 函数来等待线程结束,类似于 wait 系统调用
- pthread_join
- th:被等待线程的标识符
- thread_return:用户定义指针,用来存储被等待线程的返回值
36.3 例子
36.3.1 龟兔赛跑
1 #include <pthread.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <math.h> 5 #include <unistd.h> 6 7 typedef struct { 8 char name[20]; 9 int time; 10 int start; 11 int end; 12 }RaceArg; 13 14 /** 定义线程运行函数 */ 15 void *th_fn(void *arg) 16 { 17 RaceArg *r = (RaceArg *)arg; 18 int i = r->start; 19 20 for(; i <= r->end; i++){ 21 printf("%s(%lx) running %d\n", r->name, pthread_self(), i); 22 usleep(r->time); 23 } 24 25 return (void *)0; 26 } 27 28 int main(void) 29 { 30 int err; 31 pthread_t rabbit, turtle; ///< 定义线程标识符 32 RaceArg r_a = {"rabbit", (int )(drand48() * 100000000), 20, 50}; 33 RaceArg t_a = {"turtle", (int )(drand48() * 100000000), 10, 60}; 34 35 /** 创建 rabbit 线程 */ 36 if((err = pthread_create(&rabbit, NULL, th_fn, (void *)&r_a)) != 0){ 37 perror("pthread_create error"); 38 } 39 40 /** 创建 turtle 线程 */ 41 if((err = pthread_create(&turtle, NULL, th_fn, (void *)&t_a)) != 0){ 42 perror("pthread_create error"); 43 } 44 45 //sleep(10); 46 /** 主控线程调用 pthread_join(), 自己会阻塞,直到 rabbit 线程结束方可运行 */ 47 pthread_join(rabbit, NULL); 48 pthread_join(turtle, NULL); 49 printf("control thread id: %lx\n", pthread_self()); 50 printf("finisheld!\n"); 51 return 0; 52 }
运行结果如下:
可以看到每次都只有一个线程在执行。
在进程的线程中,每个线程的变量所在地方如下:
36.3.2 获取线程终止返回值
1 #include <pthread.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 5 typedef struct { 6 int d1; 7 int d2; 8 }Arg; 9 10 void *th_fn(void *arg) 11 { 12 Arg *r = (Arg *)arg; 13 14 /* 获取普通变量值 15 return (void *)(r->d1 + r->d2); 16 */ 17 18 /** 获取结构体对象 */ 19 return r; 20 } 21 22 int main(void) 23 { 24 int err; 25 pthread_t th; 26 Arg r = {20, 50}; 27 28 if((err = pthread_create(&th, NULL, th_fn, (void *)&r)) != 0){ 29 perror("pthread_create error"); 30 } 31 32 /** 获取普通变量值 */ 33 /* 第一种获取返回值的方法 34 int *result; 35 36 pthread_join(th, (void **)&result); 37 printf("result is %d\n", (int)result); 38 */ 39 40 /** 第二种获取返回值的方法 */ 41 /* 42 int result; 43 pthread_join(th, (void *)&result); 44 printf("result is %d\n", result); 45 */ 46 47 /** 获取结构体变量 */ 48 /* 49 int *result; 50 pthread_join(th, (void **)&result); 51 printf("result is %d\n",((Arg *)result)->d1 + ((Arg *)result)->d2); 52 */ 53 54 55 int result; 56 pthread_join(th, (void *)&result); 57 printf("result is %d\n",((Arg *)result)->d1 + ((Arg *)result)->d2); 58 59 return 0; 60 }
36.3.3 龟兔赛跑获取返回值
1 #include <pthread.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <math.h> 5 #include <unistd.h> 6 7 typedef struct { 8 char name[20]; 9 int time; 10 int start; 11 int end; 12 }RaceArg; 13 14 /** 定义线程运行函数 */ 15 void *th_fn(void *arg) 16 { 17 RaceArg *r = (RaceArg *)arg; 18 int i = r->start; 19 20 for(; i <= r->end; i++){ 21 printf("%s(%lx) running %d\n", r->name, pthread_self(), i); 22 usleep(r->time); 23 } 24 25 //return (void *)0; 26 return (void *)(r->end - r->start); 27 } 28 29 int main(void) 30 { 31 int err; 32 pthread_t rabbit, turtle; ///< 定义线程标识符 33 RaceArg r_a = {"rabbit", (int )(drand48() * 100000000), 20, 50}; 34 RaceArg t_a = {"turtle", (int )(drand48() * 100000000), 10, 60}; 35 36 /** 创建 rabbit 线程 */ 37 if((err = pthread_create(&rabbit, NULL, th_fn, (void *)&r_a)) != 0){ 38 perror("pthread_create error"); 39 } 40 41 /** 创建 turtle 线程 */ 42 if((err = pthread_create(&turtle, NULL, th_fn, (void *)&t_a)) != 0){ 43 perror("pthread_create error"); 44 } 45 46 sleep(10); 47 48 int result; 49 pthread_join(rabbit, (void *)&result); 50 printf("rabbit distance is %d\n", result); 51 pthread_join(turtle, (void *)&result); 52 printf("turtle distance is %d\n", result); 53 printf("reace finished\n"); 54 55 /** 主控线程调用 pthread_join(), 自己会阻塞,直到 rabbit 线程结束方可运行 */ 56 //pthread_join(rabbit, NULL); 57 //pthread_join(turtle, NULL); 58 59 60 printf("control thread id: %lx\n", pthread_self()); 61 printf("finisheld!\n"); 62 return 0; 63 }
来源:https://www.cnblogs.com/kele-dad/p/10202648.html