Linux线程(二)线程ID及创建线程详解

1.线程ID

就像每个进程都有一个进程 ID 一样,每个线程也有其对应的标识,称为线程 ID。进程 ID 在整个系统中是唯一的,但线程 ID 不同,线程 ID 只有在它所属的进程上下文中才有意义。

进程 ID 使用 pid_t 数据类型来表示,它是一个非负整数。而线程 ID 使用 pthread_t 数据类型来表示, 一个线程可通过库函数 pthread_self()来获取自己的线程 ID,其函数原型如下所示:

#include <pthread.h>pthread_t pthread_self(void);

使用该函数需要包含头文件<pthread.h>。

该函数调用总是成功,返回当前线程的线程 ID。

可以使用 pthread_equal()函数来检查两个线程 ID 是否相等,其函数原型如下所示:

#include <pthread.h>int pthread_equal(pthread_t t1, pthread_t t2);

如果两个线程 ID t1 和 t2 相等,则 pthread_equal()返回一个非零值;否则返回 0。在 Linux 系统中,使用无符号长整型(unsigned long int)来表示 pthread_t 数据类型,但是在其它系统当中,则不一定是无符号长整型,所以我们必须将 pthread_t 作为一种不透明的数据类型加以对待,所以 pthread_equal()函数用于比较两个线程 ID 是否相等是有用的。

线程 ID 在应用程序中非常有用,原因如下:

  • 很多线程相关函数,譬如后面将要学习的 pthread_cancel()、pthread_detach()、pthread_join()等,它们都是利用线程 ID来标识要操作的目标线程;
  • 在一些应用程序中,以特定线程的线程 ID作为动态数据结构的标签,这某些应用场合颇为有用, 既可以用来标识整个数据结构的创建者或属主线程,又可以确定随后对该数据结构执行操作的具体线程。

 2.创建线程

启动程序时,创建的进程只是一个单线程的进程,称之为初始线程或主线程,本小节我们讨论如何创建一个新的线程。

主线程可以使用库函数 pthread_create()负责创建一个新的线程,创建出来的新线程被称为主线程的子线程,其函数原型如下所示:

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

使用该函数需要包含头文件<pthread.h>。

函数参数和返回值含义如下:

thread:

pthread_t 类型指针,当 pthread_create()成功返回时,新创建的线程的线程 ID 会保存在参数 thread所指向的内存中,后续的线程相关函数会使用该标识来引用此线程。

attr:

pthread_attr_t 类型指针,指向 pthread_attr_t 类型的缓冲区,pthread_attr_t 数据类型定义了线程的各种属性,关于线程属性将会在 11.8 小节介绍。如果将参数 attr 设置为 NULL,那么表示将线程的所有属性设置为默认值,以此创建新线程。

start_routine:

参数 start_routine 是一个函数指针,指向一个函数,新创建的线程从 start_routine()函数开始运行,该函数返回值类型为void *,并且该函数的参数只有一个void *,其实这个参数就是pthread_create()函数的第四个参数 arg。如果需要向 start_routine()传递的参数有一个以上,那么需要把这些参数放到一个结构体中,然后把这个结构体对象的地址作为 arg 参数传入。

arg:

传递给 start_routine()函数的参数。一般情况下,需要将 arg 指向一个全局或堆变量,意思就是说在线程的生命周期中,该 arg 指向的对象必须存在,否则如果线程中访问了该对象将会出现错误。当然也可将参数 arg 设置为 NULL,表示不需要传入参数给 start_routine()函数。

返回值:

成功返回 0;失败时将返回一个错误号,并且参数 thread 指向的内容是不确定的。

注意 pthread_create()在调用失败时通常会返回错误码,它并不像其它库函数或系统调用一样设置 errno,每个线程都提供了全局变量 errno 的副本,这只是为了与使用 errno 到的函数进行兼容,在线程中,从函数中返回错误码更为清晰整洁,不需要依赖那些随着函数执行不断变化的全局变量,这样可以把错误的范围限制在引起出错的函数中。

线程创建成功,新线程就会加入到系统调度队列中,获取到 CPU 之后就会立马从start_routine()函数开始运行该线程的任务;调用 pthread_create()函数后,通常我们无法确定系统接着会调度哪一个线程来使用CPU 资源,先调度主线程还是新创建的线程呢(而在多核 CPU 或多 CPU 系统中,多核线程可能会在不同的核心上同时执行)?如果程序对执行顺序有强制要求,那么就必须采用一些同步技术来实现。这与前面学习父、子进程时也出现了这个问题,无法确定父进程、子进程谁先被系统调度。

使用示例使用 pthread_create()函数创建一个除主线程之外的新线程,示例代码如下所示:

//示例代码 11.3.1 pthread_create()创建线程使用示例
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>static void *new_thread_start(void *arg) {printf("新线程: 进程 ID<%d> 线程 ID<%lu>\\n", getpid(), pthread_self());return (void *)0; 
}int main(void) {pthread_t tid;int ret;ret = pthread_create(&tid, NULL, new_thread_start, NULL);if (ret) {fprintf(stderr, "Error: %s\\n", strerror(ret));exit(-1);}printf("主线程: 进程 ID<%d> 线程 ID<%lu>\\n", getpid(), pthread_self());sleep(1);exit(0);
}

应该将 pthread_t 作为一种不透明的数据类型加以对待,但是在示例代码中需要打印线程 ID,所以要明确其数据类型,示例代码中使用了 printf()函数打印线程 ID 时,将其作为 unsigned long int 数据类型,在 Linux系统下,确实是使用 unsigned long int 来表示 pthread_t,所以这样做没有问题!

主线程休眠了 1 秒钟,原因在于,如果主线程不进行休眠,它就可能会立马退出,这样可能会导致新创建的线程还没有机会运行,整个进程就结束了。

在主线程和新线程中,分别通过 getpid()和 pthread_self()来获取进程 ID 和线程 ID,将结果打印出来,运行结果如下所示:

编译时出现了错误,提示“对‘pthread_create’未定义的引用”,示例代码确实已经包含了<pthread.h>头文件,但为什么会出现这样的报错,仔细看,这个报错是出现在程序代码链接时、而并非是编译过程,所以可知这是链接库的文件,如何解决呢?

gcc -o testApp testApp.c -lpthread

使用-l 选项指定链接库 pthread,原因在于 pthread 不在 gcc 的默认链接库中,所以需要手动指定。再次编译便不会有问题了,如下:

从打印信息可知,正如前面所介绍那样,两个线程的进程 ID 相同,说明新创建的线程与主线程本来就属于同一个进程,但是它们的线程 ID 不同。从打印结果可知,Linux 系统下线程 ID 数值非常大,看起来像是一个指针。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1553145.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

记录cocoscreater3.8.x设置2d卡牌圆角

引擎版本&#xff1a;Cocos Creater3.8.3版本 1.在Card节点上添加Mask组件&#xff0c;类型选择 2.在Card节点上绑定CardController.ts脚本 3.在CardController.ts编写圆角脚本&#xff0c;其实就是动态绘制Graphics组件 import { _decorator, Color, Component, Graphics, …

排序01 多目标模型

引入 使用机器学习方法对指标做预估&#xff0c;再对预估分数做融合。融合方法&#xff1a;加权和方法给不同指标赋予不同的权重&#xff0c;权重是做A/B test调试得到的。还有更好地融合方法。 多目标模型 排序模型的输入是各种各样的特征&#xff0c;用户特征主要是用户id和…

ADRC与INDI的关系

ADRC与INDI的关系 前言 一直热衷于把一些基础的东西想明白&#xff0c;这样才能更好地理解一些稍微复杂些的算法&#xff0c;在深入理解这些算法后才能更好地应用。 例如 用回路成型方法探究ADRC各参数对闭环系统的影响对比KF和RLS的关系互补滤波的原理以及参数整定&#xf…

【Python报错已解决】TypeError: not enough arguments for format string

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

C++【类和对象】(再探构造函数、类型转换与static成员)

文章目录 1. 再探构造函数2. 类型转换3. static成员结语 1. 再探构造函数 之前我们实现构造函数时&#xff0c;初始化成员变量主要使用函数体内赋值&#xff0c;构造函数初始化还有⼀种方式&#xff0c;就是初始化列表&#xff0c;初始化列表的使用方式是以⼀个冒号开始&#…

体系结构论文(五十三):Featherweight Soft Error Resilience for GPUs 【22‘ MIRCO】

Featherweight Soft Error Resilience for GPUs 一、文章介绍 背景&#xff1a;软错误通常由高能粒子&#xff08;如宇宙射线和α粒子&#xff09;打击电路造成的位翻转&#xff0c;可能导致程序崩溃或产生错误输出。随着电子技术的进步&#xff0c;电路对这种辐射引发的软错…

电子连接器温升仿真教程 二

在《电子连接器温升仿真教程 一》中详细介绍了用内热法做电子连接器温升仿真的操作步骤与方法,本教程将讲解用电流电压法做电子连接器温升仿真。 本教程,将以下面产品为例演示温升仿真方法其操作步骤。 该连接器为电池连接器,其Housing材料为LCP+30%GF,端子材质为铍铜…

Linux相关概念和重要知识点(11)(进程调度、Linux内核链表)

1.Linux调度算法 上篇文章我粗略讲过queue[140]的结构&#xff0c;根据哈希表&#xff0c;我们可以将40个不同优先级的进程借助哈希桶链入queue[140]中。调度器会根据queue的下标来进行调度。但这个具体的调度过程是怎样的呢&#xff1f;以及runqueue和queue[140]的关系是什么…

[C++] 剖析AVL树功能的实现原理

文章目录 引言AVL树的关键性质为什么选择AVL树&#xff1f; AVL树的结构节点对象的类 AVL树的插入检查是否为空树并处理根节点查询插入位置&#xff08;非递归&#xff09;插入节点并连接父节点更新平衡因子&#xff08;在失去平衡的条件下进行旋转&#xff09; 旋转旋转的原则…

基于ssm的学生社团管理系统 社团分配系统 社团活动调度平台 学生社团管理 信息化社团管理开发项目 社团活动管理 社团预约系统(源码+文档+定制)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

VMware中Ubuntu系统Docker正常运行但网络不通(已解决)

问题描述&#xff1a;在VMware中的Ubuntu系统下部署了Docker&#xff0c;当在docker容器中运行Eureka微服务时&#xff0c;发现Eureka启动正常&#xff0c;但无法通过网页访问该容器中Eureka。 解决办法如下&#xff1a; 1、创建桥接网络&#xff1a;test-net sudo docker n…

一文带你入门客制化键盘,打造专属打字利器

我用过不少键盘&#xff0c;但是都不太符合自己的需求&#xff0c;最后还是走向了客制化。 客制化&#xff0c;可以理解为自定义、DIY&#xff0c;自己动手拼装出一把只属于自己的键盘。 本文会对客制化做个简单的介绍&#xff0c;旨在读者能自己简单拼装出一款键盘。 目前市…

QStyle文档

前言 本文翻译自Qt官方文档&#xff0c;详细介绍了各成员/类型的作用&#xff0c;包含部分示例代码。 QStyle类的内容非常庞大&#xff0c;如需快速了解类成员和使用简介&#xff0c;请参见 QStyle简介。 一、QStyle Class QStyle是一个抽象基类&#xff0c;封装了GUI的外观…

OpenGL笔记之事件驱动设计将相机控制类和应用程序类分离

OpenGL笔记之事件驱动设计将相机控制类和应用程序类分离 —— 2024-10-02 下午 code review! 文章目录 OpenGL笔记之事件驱动设计将相机控制类和应用程序类分离1.代码图片2.分析3.UML4.代码 1.代码图片 运行 Mouse button 1 pressed at (100, 200) Mouse dragged by (50, 50)…

Spring(学习笔记)

<context:annotation-config/>是 Spring 配置文件中的一个标签&#xff0c;用于开启注解配置功能。这个标签可以让 Spring 容器识别并处理使用注解定义的 bean。例如&#xff0c;可以使用 Autowired 注解自动装配 bean&#xff0c;或者使用 Component 注解将类标记为 bea…

10/02赛后总结

T1学习除法 题目传送门&#xff1a;学习除法http://bbcoj.cn/contest/1028/problem/1 说白了&#xff0c;就是检验是不是质数罢了&#xff0c;是质数输出0&#xff0c;不然输出1&#xff1b; 但是质数判断写错了 100分只有60分&#xff0c;damn #include<bits/stdc.h>…

【Linux】进程间关系与守护进程

超出能力之外的事&#xff0c; 如果永远不去做&#xff0c; 那你就永远无法进步。 --- 乌龟大师 《功夫熊猫》--- 进程间关系与守护进程 1 进程组2 会话3 控制终端4 作业控制5 守护进程 1 进程组 之前我们提到了进程的概念&#xff0c; 其实每一个进程除了有一个进程 ID(P…

Django5 使用pyinstaller打包成 exe服务

首先&#xff1a;确保当前的django项目可以完美运行&#xff0c;再进行后续操作 python manage.py runserver第一步 安装 pyinstaller pip install pyinstaller第二步 创建spec 文件 pyinstaller --name manage --onefile manage.pypyinstaller&#xff1a;这是调用 PyInsta…

数据异质性与数据异构性的本质和举例说明

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 在现代数据科学与信息技术领域&#xff0c;“数据异质性” 与 “数据异构性” 是两个常见的概念。对于初学者而言&#xff0c;明确这两个概念的本质及其间的差异至关重要。本文旨在以简明易懂的方式&am…