zoukankan      html  css  js  c++  java
  • 三十六、Linux 线程——线程基本概念及线程的创建和终止

    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
    ", 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
    ", pthread_self());
    50     printf("finisheld!
    ");
    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
    ", (int)result);
    38     */
    39 
    40     /** 第二种获取返回值的方法 */
    41     /*
    42     int result;
    43     pthread_join(th, (void *)&result);
    44     printf("result is %d
    ", result);
    45     */
    46 
    47     /** 获取结构体变量 */
    48     /*
    49     int *result;
    50     pthread_join(th, (void **)&result);
    51     printf("result is %d
    ",((Arg *)result)->d1 + ((Arg *)result)->d2);
    52     */
    53 
    54 
    55     int result;
    56     pthread_join(th, (void *)&result);
    57     printf("result is %d
    ",((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
    ", 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
    ", result);
    51     pthread_join(turtle, (void *)&result);
    52     printf("turtle distance is %d
    ", result);
    53     printf("reace finished
    ");
    54 
    55     /** 主控线程调用 pthread_join(), 自己会阻塞,直到 rabbit 线程结束方可运行 */
    56     //pthread_join(rabbit, NULL);
    57     //pthread_join(turtle, NULL);
    58     
    59 
    60     printf("control thread id: %lx
    ", pthread_self());
    61     printf("finisheld!
    ");
    62     return 0;
    63 }
  • 相关阅读:
    python第三周练习
    python第一周作业
    SQLite3—数据库的学习—python
    python实现跳一跳辅助的实验报告
    Python——自己的第一个网页(文件的使用)
    第一次爬虫和测试
    numpy和matplotlib使用
    Python作业———预测球队比赛成绩
    PIL库的学习
    Pytho作业——Jieba库的使用和好玩的词云
  • 原文地址:https://www.cnblogs.com/kele-dad/p/10202648.html
Copyright © 2011-2022 走看看