三十六、Linux 线程——线程基本概念及线程的创建和终止

时间:2021-08-30 05:56:26

36.1 线程介绍

36.1.1 线程的基本概念

  • 进程是资源管理的最小单位,线程是程序执行的最小单位
  • 每个进程都有自己的数据段、代码段和堆栈段。
  • 线程通常叫做轻型的进程,它包含独立的栈和 CPU 寄存器状态,线程是进程的一条执行路径,每个线程共享其所附属进程的所有资源,包括打开的文件、内存页面、信号标识及动态分配的内存等。
  • 因为线程和进程比起来很小,所以相对来说,线程花费更少的 CPU 资源
  • 在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器,并且减少进程上下文切换的开销。

36.1.2 进程和线程的关系

  • 线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一用户内存空间,当进程退出时,该进程所产生的线程都会被强制退出并清除。
  • 一个进程至少需要一个线程作为它的指令执行,进程管理着资源(比如 CPU、内存、文件等等),并将线程分配到某个 CPU 上执行

  三十六、Linux 线程——线程基本概念及线程的创建和终止

36.1.3 线程分类

  • 线程按照其调度者可分为用户级线程和内核级线程两种:
    • 用户级线程:主要解决的是上下文切换的问题,其调度过程由用户决定
    • 内核级线程:由内核调度机制实现
  • 现在大多数操作系统都采用用户级线程和内核级线程并存的方法
  • 用户级线程要绑定内核级线程运行,一个进程中的内核级线程会分配到固定的时间片,用户级线程分配的时间片以内核级线程为准
  • 默认情况下,用户级线程和内核级线程是一对一,也可以多对一,这样实时性就会比较差
  • 当 CPU 分配给线程的时间片用完后但线程没有执行完毕,此时线程会从运行状态返回到就绪状态,将 CPU 让给其他线程使用

36.1.4 Linux 线程实现

  • 以下线程均为用户级线程
    • 在Linux 中,一般采用 pthread 线程库实现线程的访问与控制,由 POSIX 提出,具有良好的可移植性
  • Linux 线程程序编译需要在 gcc 上链接库 pthread

36.1.5 线程标识

  • 每个进程内部的不同线程都由自己的唯一标识(ID)
  • 线程标识只在它所属的进程环境中有效
  • 线程标识是 pthread_t 数据类型
 #include <pthread.h>
int pthread_equal(pthread_t, pthread_t);
  • 函数功能:判断两个线程是否相等
  • 返回值:相等返回非0;否则返回0
 #include <pthread.h>
pthread_t pthread_self(void);
  • 函数功能:获取当前线程的线程 ID
  • 返回值:调用线程的线程 ID

36.2 线程的创建和销毁

36.2.1 线程创建

 #include <pthread.h>
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
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)
 #include <pthread.h>
int pthread_cancel(pthread_t tid);
void pthread_exit(void *retval);
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 龟兔赛跑

 #include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h> typedef struct {
char name[];
int time;
int start;
int end;
}RaceArg; /** 定义线程运行函数 */
void *th_fn(void *arg)
{
RaceArg *r = (RaceArg *)arg;
int i = r->start; for(; i <= r->end; i++){
printf("%s(%lx) running %d\n", r->name, pthread_self(), i);
usleep(r->time);
} return (void *);
} int main(void)
{
int err;
pthread_t rabbit, turtle; ///< 定义线程标识符
RaceArg r_a = {"rabbit", (int )(drand48() * 100000000), 20, 50};
RaceArg t_a = {"turtle", (int )(drand48() * ), , }; /** 创建 rabbit 线程 */
if((err = pthread_create(&rabbit, NULL, th_fn, (void *)&r_a)) != ){
perror("pthread_create error");
} /** 创建 turtle 线程 */
if((err = pthread_create(&turtle, NULL, th_fn, (void *)&t_a)) != ){
perror("pthread_create error");
} //sleep(10);
/** 主控线程调用 pthread_join(), 自己会阻塞,直到 rabbit 线程结束方可运行 */
pthread_join(rabbit, NULL);
pthread_join(turtle, NULL);
printf("control thread id: %lx\n", pthread_self());
printf("finisheld!\n");
return ;
}

  运行结果如下:

  三十六、Linux 线程——线程基本概念及线程的创建和终止  三十六、Linux 线程——线程基本概念及线程的创建和终止

  可以看到每次都只有一个线程在执行。

  在进程的线程中,每个线程的变量所在地方如下:

  三十六、Linux 线程——线程基本概念及线程的创建和终止

36.3.2 获取线程终止返回值

 #include <pthread.h>
#include <stdlib.h>
#include <stdio.h> typedef struct {
int d1;
int d2;
}Arg; void *th_fn(void *arg)
{
Arg *r = (Arg *)arg; /* 获取普通变量值
return (void *)(r->d1 + r->d2);
*/ /** 获取结构体对象 */
return r;
} int main(void)
{
int err;
pthread_t th;
Arg r = {, }; if((err = pthread_create(&th, NULL, th_fn, (void *)&r)) != ){
perror("pthread_create error");
} /** 获取普通变量值 */
/* 第一种获取返回值的方法
int *result; pthread_join(th, (void **)&result);
printf("result is %d\n", (int)result);
*/ /** 第二种获取返回值的方法 */
/*
int result;
pthread_join(th, (void *)&result);
printf("result is %d\n", result);
*/ /** 获取结构体变量 */
/*
int *result;
pthread_join(th, (void **)&result);
printf("result is %d\n",((Arg *)result)->d1 + ((Arg *)result)->d2);
*/ int result;
pthread_join(th, (void *)&result);
printf("result is %d\n",((Arg *)result)->d1 + ((Arg *)result)->d2); return ;
}

36.3.3 龟兔赛跑获取返回值

 #include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h> typedef struct {
char name[];
int time;
int start;
int end;
}RaceArg; /** 定义线程运行函数 */
void *th_fn(void *arg)
{
RaceArg *r = (RaceArg *)arg;
int i = r->start; for(; i <= r->end; i++){
printf("%s(%lx) running %d\n", r->name, pthread_self(), i);
usleep(r->time);
} //return (void *)0;
return (void *)(r->end - r->start);
} int main(void)
{
int err;
pthread_t rabbit, turtle; ///< 定义线程标识符
RaceArg r_a = {"rabbit", (int )(drand48() * 100000000), 20, 50};
RaceArg t_a = {"turtle", (int )(drand48() * ), , }; /** 创建 rabbit 线程 */
if((err = pthread_create(&rabbit, NULL, th_fn, (void *)&r_a)) != ){
perror("pthread_create error");
} /** 创建 turtle 线程 */
if((err = pthread_create(&turtle, NULL, th_fn, (void *)&t_a)) != ){
perror("pthread_create error");
} sleep(); int result;
pthread_join(rabbit, (void *)&result);
printf("rabbit distance is %d\n", result);
pthread_join(turtle, (void *)&result);
printf("turtle distance is %d\n", result);
printf("reace finished\n"); /** 主控线程调用 pthread_join(), 自己会阻塞,直到 rabbit 线程结束方可运行 */
//pthread_join(rabbit, NULL);
//pthread_join(turtle, NULL); printf("control thread id: %lx\n", pthread_self());
printf("finisheld!\n");
return ;
}

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

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

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

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

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

  3. Java进阶&lpar;三十六&rpar;深入理解Java的接口和抽象类

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

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

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

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

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

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

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

  7. Gradle 1&period;12用户指南翻译——第三十六章&period; 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. &OpenCurlyDoubleQuote;全栈2019”Java第三十六章:类

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

随机推荐

  1. php中抓取网页内容的代码

    方法一: 使用file_get_contents方法实现 $url = "http://news.sina.com.cn/c/nd/2016-10-23/doc-ifxwztru695114 ...

  2. 如何解析复杂的C语言声明

    C语言中有时会出现复杂的声明,比如   char * const * (*next) (); //这是个什么东东?   在讲复杂声明的分析方法前,先来个补充点.   C语言变量的声明始终贯彻两点 :  ...

  3. 字符串操作函数&lt&semi;string&period;h&gt&semi;相关函数strcpy&comma;strcat&comma;等源码。

    首先说一下源码到底在哪里找. 我们在文件中包含<cstring>时,如果点击右键打开文档, 会打开cstring,我们会发现路径为: D:\Program Files\visual stu ...

  4. gameUnity 网络游戏框架

    常常在想,有没有好的方式,让开发变得简单,让团队合作更加容易. 于是,某一天 动手写一个 架构, 目前版本 暂定 0.1 版本.(unity5.0.0f4 版本以上) 我打算 开源出来 0.1有什么功 ...

  5. centos7虚拟机设置静态ip

    cd /etc/sysconfig/network-scripts/ vi ifcfg-enp0s3 1.修改配置 BOOTPROTO=static2.新增配置(查询宿主机对应的信息) IPADDR= ...

  6. MySQL创建方法错误&colon;This function has none of DETERMINISTIC&comma; NO SQL

    创建function时 出错信息: ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL ...

  7. npm 常用命令详解(转载)

    学习gulp的使用时,对npm的掌握是必不可少的,经常到npm官网查询文档让我感到不爽,还不如整理了一些常用的命令博客上,于是根据自己的理解简单翻译过来,终于有点输出,想学习npm这块的朋友不可错过这 ...

  8. java注释讲解

    注释简单的来说就是一种说明,不能被当成执行语句执行.做为一名程序员,但我们在写代码时是顺着思路写下去的.写代码好比就是在做题.当你在做的时候你脑海时的思路很清晰,会想到用一些特殊的方法来解决当前的问题 ...

  9. 平行四边形 css实现

    首先将 display 设置为  inline-block 或block: 在应用skew(): transform:skewX(-45deg); 但是也会导致平行四边形内的文字倾斜如下 我们可以给文 ...

  10. Firebird3 多文件支持

    默认建立数据库时为一个数据文件,但文件不能无限大,故可以为数据库增加新文件: isql 打开数据库,并conn到指定数据库,然后 Alter databaseAdd file ‘d:\data\d2. ...