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[];
  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 *);
  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() * ), , };
  34.  
  35. /** 创建 rabbit 线程 */
  36. if((err = pthread_create(&rabbit, NULL, th_fn, (void *)&r_a)) != ){
  37. perror("pthread_create error");
  38. }
  39.  
  40. /** 创建 turtle 线程 */
  41. if((err = pthread_create(&turtle, NULL, th_fn, (void *)&t_a)) != ){
  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 ;
  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 = {, };
  27.  
  28. if((err = pthread_create(&th, NULL, th_fn, (void *)&r)) != ){
  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. int result;
  55. pthread_join(th, (void *)&result);
  56. printf("result is %d\n",((Arg *)result)->d1 + ((Arg *)result)->d2);
  57.  
  58. return ;
  59. }

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[];
  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() * ), , };
  35.  
  36. /** 创建 rabbit 线程 */
  37. if((err = pthread_create(&rabbit, NULL, th_fn, (void *)&r_a)) != ){
  38. perror("pthread_create error");
  39. }
  40.  
  41. /** 创建 turtle 线程 */
  42. if((err = pthread_create(&turtle, NULL, th_fn, (void *)&t_a)) != ){
  43. perror("pthread_create error");
  44. }
  45.  
  46. sleep();
  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. printf("control thread id: %lx\n", pthread_self());
  60. printf("finisheld!\n");
  61. return ;
  62. }

三十六、Linux 线程——线程基本概念及线程的创建和终止的更多相关文章

  1. Linux学习之CentOS(二十六)--Linux磁盘管理:LVM逻辑卷的创建及使用

    在上一篇随笔里面 Linux学习之CentOS(二十五)--Linux磁盘管理:LVM逻辑卷基本概念及LVM的工作原理,详细的讲解了Linux的动态磁盘管理LVM逻辑卷的基本概念以及LVM的工作原理, ...

  2. “全栈2019”Java多线程第二十六章:同步方法生产者与消费者线程

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  3. Java进阶(三十六)深入理解Java的接口和抽象类

    Java进阶(三十六)深入理解Java的接口和抽象类 前言 对于面向对象编程来说,抽象是它的一大特征之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类.这两者有太多相似的地方,又有太 ...

  4. 《手把手教你》系列技巧篇(三十六)-java+ selenium自动化测试-单选和多选按钮操作-番外篇(详解教程)

    1.简介 前边几篇文章是宏哥自己在本地弄了一个单选和多选的demo,然后又找了网上相关联的例子给小伙伴或童鞋们演示了一下如何自动化测试,这一篇宏哥在网上找了一个问卷调查,给小伙伴或童鞋们来演示一下.上 ...

  5. 程序员编程艺术第三十六~三十七章、搜索智能提示suggestion,附近点搜索

    第三十六~三十七章.搜索智能提示suggestion,附近地点搜索 作者:July.致谢:caopengcs.胡果果.时间:二零一三年九月七日. 题记 写博的近三年,整理了太多太多的笔试面试题,如微软 ...

  6. NeHe OpenGL教程 第三十六课:从渲染到纹理

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  7. Gradle 1.12用户指南翻译——第三十六章. Sonar Runner 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  8. 第三百三十六节,web爬虫讲解2—urllib库中使用xpath表达式—BeautifulSoup基础

    第三百三十六节,web爬虫讲解2—urllib库中使用xpath表达式—BeautifulSoup基础 在urllib中,我们一样可以使用xpath表达式进行信息提取,此时,你需要首先安装lxml模块 ...

  9. centos shell脚本编程2 if 判断 case判断 shell脚本中的循环 for while shell中的函数 break continue test 命令 第三十六节课

    centos  shell脚本编程2 if 判断  case判断   shell脚本中的循环  for   while   shell中的函数  break  continue  test 命令   ...

  10. “全栈2019”Java第三十六章:类

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

随机推荐

  1. 「JLOI2015」管道连接 解题报告

    「JLOI2015」管道连接 先按照斯坦纳树求一个 然后合并成斯坦纳森林 直接枚举树的集合再dp一下就好了 Code: #include <cstdio> #include <cct ...

  2. [SCOI2010]幸运数字(容斥+爆搜)

    在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是 ...

  3. 【php】php算法的一些实现

    1.生成一定范围内的数组,步长可以设置 function range($step = 10, $max = 100){ if ($step < 1 || $step > $max){ re ...

  4. poj 1144 (Tarjan求割点数量)

    题目链接:http://poj.org/problem?id=1144 描述 一个电话线公司(简称TLC)正在建立一个新的电话线缆网络.他们连接了若干个地点分别从1到N编号.没有两个地点有相同的号码. ...

  5. 生成SSH密钥添加到GitHub

    将自己的过程和踩坑写下来 一.检查是否有SSH 1.打开Git的目录文件,下图是我电脑中git的文件目录 2.点击git-bash.exe,输入ssh查看电脑中是否已存在ssh,会出现以下结果,证明已 ...

  6. MongoDB 学习记录(二)yum安装

    前言:接着上篇继续学习MongoDB,这次学习的是在Linux下安装MongoDB 环境:centos7.3 安装版本:MongoDB4.0 官网安装教程地址 https://docs.mongodb ...

  7. 从 date 中取出 小时和分钟进行比较

    public class T1 { public static void main(String[] args) throws ParseException { SimpleDateFormat df ...

  8. hdu 4283"You Are the One"(区间DP)

    传送门 https://www.cnblogs.com/violet-acmer/p/9852294.html 题意: 有n个屌丝排成一排,每个屌丝都有一个不开心值a[ i ]( i=1,2,3,.. ...

  9. app软件遵循的规范

    http://www.jianshu.com/p/a2a4c18c1900 https://wenku.baidu.com/view/ecb09b07a4e9856a561252d380eb6294d ...

  10. (set) 人见人爱A-B hdu2034

    人见人爱A-B Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Su ...