Linux中的线程(一)-- 线程的创建

2023-11-08

什么是线程?

Linux 中的线程被称为“轻量级进程”(Lightweight Process,LWP),它是在进程内部运行的一种“子进程”。与传统的进程不同,线程共享相同的虚拟地址空间和其他资源,例如打开的文件、信号处理程序和用户 ID 等。

线程具有哪些特点

  1. 轻量级:
    因为线程与父进程共享资源,所以创建和销毁线程的开销要比创建和销毁进程的开销要小得多。
  2. 调度:
    Linux 线程是由内核进行调度的,线程的调度是基于调度策略和优先级来完成的。常见的调度策略包括 Round Robin、FIFO 和实时调度等。
  3. 同步:
    由于线程共享同一进程的地址空间,所以线程之间可以通过共享变量进行通信。Linux 提供了许多同步机制,例如信号量、互斥量和条件变量等,以确保线程之间的同步和互斥。
  4. 线程安全:
    Linux 系统提供了一些线程安全的库和函数,例如 pthread 库,这些库和函数可以在多线程程序中使用,避免竞态条件和其他线程安全问题。
  5. 调试:
    Linux 系统提供了一些工具来调试多线程程序,例如 gdb 调试器和 strace 工具。

线程与进程有什么区别

简单的说,进程是程序资源分配的最小单位,线程是程序执行的最小单位。

  1. 资源共享:
    进程是系统分配资源的基本单位,每个进程都有自己独立的地址空间、文件描述符、信号处理等资源,不同进程之间的资源不共享。而线程则共享同一个进程的地址空间和资源,它们之间可以相互访问共享变量,共享同一进程的文件和打开的网络连接等。
  2. 创建销毁开销:
    创建和销毁进程的开销比创建和销毁线程的开销要大。因为每个进程都有自己独立的地址空间和系统资源,创建一个新的进程需要操作系统分配新的内存空间、建立内核数据结构等,而线程则共享其所属进程的资源,因此创建和销毁线程的开销要比创建和销毁进程的开销要小得多。
  3. 上下文切换:
    线程之间的上下文切换比进程之间的上下文切换要快得多。因为线程共享相同的地址空间和资源,切换时只需要保存和恢复少量的寄存器状态即可,而进程之间的上下文切换需要保存和恢复更多的状态信息,比如虚拟内存空间等。
  4. 并发性和资源竞争
    线程之间的并发性比进程之间的并发性更高,因为它们共享同一进程的地址空间和资源,同时也会导致线程之间发生资源竞争的问题,例如竞争共享变量的访问权等。而进程之间的并发性相对较低,因为它们彼此独立。

如何创建一个线程

Talk is cheap,直接上代码!

#include <iostream>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

using namespace std;


void *th_fun(void *arg)
{
    int *p = (int *)arg;
    printf("thread PID = %d\n",getpid());//获取本进程的ID
    printf("thread pthread ID = %d\n",pthread_self());//获取函数自己的ID
    printf("thread PARAMETER = %d\n",*p);//打印传入的参数
}

int main(void)
{
    pthread_t pid;//线程ID
    void *tret;
    int n=10;

    pthread_create(&pid,NULL,th_fun,(void*)&n);//pid 线程ID,传出参数
    pthread_join(pid, &tret);//主线程阻塞等待子线程的终止
    printf("MAIN pthread ID = %x\n",pthread_self());//主控线程的ID号
    printf("MAIN create pthread ID = %x\n",pid);//线程的ID
    printf("MAIN PID = %d\n",getpid());//进程ID

    return 0;
}

解读

主函数通过pthread_create函数创建一个线程,线程的函数名为th_fun,同时有一个pid参数代表线程的ID号,通过pthread_t类型定义。这里在pthread_create最后一个参数中,用了一个n变量负责传入一个数据给线程函数,然后在线程函数中打印出来。
pthread_join负责将线程设置为分离态,负责回收线程的资源。
主函数中分别打印了三个内容,主进程的线程的ID号,创建的新线程的ID号,主进程的ID号。
th_fun内容则简单的多,这个新创建的执行线程只负责打印三个内容:创建本线程的进程ID号,本线程自己的线程ID号,传入的arg参数。

输出

thread PID = 14420
thread pthread ID = 2
thread PARAMETER = 10
MAIN pthread ID = 1
MAIN create pthread ID = 2
MAIN PID = 14420

这里主进程的线程ID号为1,新创建的线程ID为2,该主函数执行时创建的进程ID为14420。
先打印出来thread线程的内容:因为线程是由main函数中创建的,所以进程ID同样为14420。新创建的线程ID和主函数中获取的线程ID一样为2,参数10是通过pthread_create的最后一个参数传进来的,这里要注意最后一个参数的类型为 void* 型,所以在赋值的时候也要将变量转换成 void* 型。

线程原语

实际上和线程有关的函数特别多,通过man命令可以查看到有哪些线程原语:

root@ubuntu:/home# man -k pthread
pthread_attr_destroy (3) - initialize and destroy thread attributes object
pthread_attr_getaffinity_np (3) - set/get CPU affinity attribute in thread attributes object
pthread_attr_getdetachstate (3) - set/get detach state attribute in thread attributes object
pthread_attr_getguardsize (3) - set/get guard size attribute in thread attributes object
pthread_attr_getinheritsched (3) - set/get inherit-scheduler attribute in thread attributes object
pthread_attr_getschedparam (3) - set/get scheduling parameter attributes in thread attributes object
pthread_attr_getschedpolicy (3) - set/get scheduling policy attribute in thread attributes object
pthread_attr_getscope (3) - set/get contention scope attribute in thread attributes object
pthread_attr_getstack (3) - set/get stack attributes in thread attributes object
pthread_attr_getstackaddr (3) - set/get stack address attribute in thread attributes object
pthread_attr_getstacksize (3) - set/get stack size attribute in thread attributes object
pthread_attr_init (3) - initialize and destroy thread attributes object
pthread_attr_setaffinity_np (3) - set/get CPU affinity attribute in thread attributes object
pthread_attr_setdetachstate (3) - set/get detach state attribute in thread attributes object
pthread_attr_setguardsize (3) - set/get guard size attribute in thread attributes object
pthread_attr_setinheritsched (3) - set/get inherit-scheduler attribute in thread attributes object
pthread_attr_setschedparam (3) - set/get scheduling parameter attributes in thread attributes object
pthread_attr_setschedpolicy (3) - set/get scheduling policy attribute in thread attributes object
pthread_attr_setscope (3) - set/get contention scope attribute in thread attributes object
pthread_attr_setstack (3) - set/get stack attributes in thread attributes object
pthread_attr_setstackaddr (3) - set/get stack address attribute in thread attributes object
pthread_attr_setstacksize (3) - set/get stack size attribute in thread attributes object
pthread_cancel (3)   - send a cancellation request to a thread
...

由于篇幅限制,后面的一部分就省略掉了,读者可以自行去查看。

那我们这里用到的几个和线程有关的线程原语有:

pthread_create

这个函数负责的是创建线程,通过man命令可以获取如何使用该函数。

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

分别解释一下变量的含义:

  • pthread_t *thread:传递一个pthread_t变量地址进来,用于保存新线程的tid(线程ID)
  • const pthread_attr_t *attr:线程属性设置,如使用默认属性,则传NULL
  • void *(*start_routine) (void *):函数指针,指向新线程应该加载执行的函数模块
  • void *arg:指定线程将要加载调用的那个函数的参数
  • 返回值:成功返回0,失败返回错误号。以前学过的系统函数都是成功返回0,失败返回-1,而错误号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有一个errno,但这是为了兼容其它函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰
  • attr参数表示线程属性,本节不深入讨论线程属性,所有代码例子都传NULL给attr参数,表示线程属性取缺省值,感兴趣的读者可以参考[APUE2e]

pthread_join

调用该函数的线程将挂起等待,直到id为thread的线程终止。也就是在子线程调用了pthread_join()方法后面的代码,只有等到子线程结束了才能执行。

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
  • pthread_t thread:回收线程的tid
  • void **retval:接收退出线程传递出的返回值
  • 返回值:成功返回0,失败返回错误号

注意,这里其实还涉及到与pthread_detach()函数的区别对比。在后续的文章再加以对比说明。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux中的线程(一)-- 线程的创建 的相关文章

  • 如何从脚本编辑 /etc/sudoers?

    我需要编辑 etc sudoers从脚本中添加 删除白名单中的内容 假设我有一个可以处理普通文件的命令 我如何将其应用到 etc sudoers 我可以复制并修改它 然后有visudo用修改后的副本替换原始版本 通过提供我自己的脚本 EDI
  • 应用程序中两个不同版本的库

    考虑一个场景 其中有两个不同版本的共享库 考虑 A 1 so 链接到 B so A 2 so 链接到 C so 现在 B so 和 C so 都链接到 d exe 当 B so 想要调用 A 1 so 中的函数时 它最终会调用 A 2 so
  • 如何在C(Linux utf8终端)中打印“盒子抽屉”Unicode字符?

    我正在尝试显示 方框图范围 2500 257F 中的 Unicode 字符 它应该是标准 utf8 Unicode 标准 版本 6 2 我根本做不到 我首先尝试使用旧的 ASCII 字符 但 Linux 终端以 utf8 显示 并且没有显示
  • 使用 ioctl 在 C++ 中以编程方式添加路由

    我编写了简单的 C 函数 添加了新路线 void addRoute int fd socket PF INET SOCK DGRAM IPPROTO IP struct rtentry route memset route 0 sizeof
  • 为什么 OS X 和 Linux 之间的 UTF-8 文本排序顺序不同?

    我有一个包含 UTF 8 编码文本行的文本文件 mac os x cat unsorted txt foo foo 津 如果它有助于重现问题 这里是文件中确切字节的校验和和转储 以及如何自己生成文件 在 Linux 上 使用base64 d
  • Docker忽略limits.conf(试图解决“打开文件太多”错误)

    我正在运行一个 Web 服务器 该服务器正在处理数千个并发 Web 套接字连接 为了实现这一点 在 Debian linux 我的基本镜像是 google debian wheezy 在 GCE 上运行 上 打开文件的默认数量设置为 100
  • 链接错误:命令行中缺少 DSO

    我对 Linux 使用 Ubuntu 14 04 LTS 64 位 相当陌生 来自 Windows 并且正在尝试移植我现有的 CUDA 项目 当通过链接时 usr local cuda bin nvcc arch compute 30 co
  • Linux shell 从用户输入中获取设备 ID

    我正在为一个程序编写安装脚本 该程序需要在其配置中使用 lsusb 的设备 ID 因此我正在考虑执行以下操作 usblist lsusb put the list into a array for each line use the arr
  • 使用 libusb 输出不正确

    我用libusb编写了一个程序 我怀疑输出是否正确 因为所有条目都显示相同的供应商和产品 ID 以下是代码 include
  • 如何并行执行4个shell脚本,我不能使用GNU并行?

    我有4个shell脚本dog sh bird sh cow sh和fox sh 每个文件使用 xargs 并行执行 4 个 wget 来派生一个单独的进程 现在我希望这些脚本本身能够并行执行 由于某些我不知道的可移植性原因 我无法使用 GN
  • LINUX:如何锁定内存中进程的页面

    我有一个 LINUX 服务器 运行一个具有大量内存占用的进程 某种数据库引擎 该进程分配的内存太大 需要将其中一部分换出 换出 我想做的是将所有其他进程 或正在运行的进程的子集 的内存页面锁定在内存中 以便只有数据库进程的页面被换出 例如
  • 嵌入清单文件以要求具有 mingw32 的管理员执行级别

    我正在 ubuntu 下使用 i586 mingw32msvc 交叉编译应用程序 我很难理解如何嵌入清单文件以要求 mingw32 具有管理员执行级别 对于我的例子 我使用了这个hello c int main return 0 这个资源文
  • 如何从 C++ 程序中重新启动 Linux?

    我有一个 Qt 4 GUI 我需要在下拉菜单中提供一个选项 允许用户选择重新启动计算机 我意识到这对于以其他方式重新启动计算机的能力来说似乎是多余的 但选择需要保留在那里 我尝试使用 system 来调用以下内容 suid root she
  • 来自守护程序的错误响应:加入会话密钥环:创建会话密钥:超出磁盘配额

    我尝试在我的服务器上安装 docker 使用本教程 https docs docker com install linux docker ce ubuntu 我想远程运行 docker 镜像并使用 portainer Web 界面来管理一切
  • 在 docker 中重定向命令输出

    我想为我的服务器做一些简单的日志记录 它是一个在 Docker 容器中运行的小型 Flask 应用程序 这是 Dockerfile Dockerfile FROM dreen flask MAINTAINER dreen WORKDIR s
  • ssh 连接超时

    我无法在 git 中 ssh 到 github bitbucket 或 gitlab 我通常会收到以下错误消息 如何避免它 输出 ssh T email protected cdn cgi l email protection i ssh
  • vmsplice() 和 TCP

    在原来的vmsplice 执行 有人建议 http lwn net Articles 181169 如果您的用户态缓冲区是管道中可容纳的最大页面数的 2 倍 则缓冲区后半部分成功的 vmsplice 将保证内核使用缓冲区的前半部分完成 但事
  • ioctl 命令的用户权限检查

    我正在实现 char 驱动程序 Linux 并且我的驱动程序中有某些 IOCTL 命令仅需要由 ADMIN 执行 我的问题是如何在 ioctl 命令实现下检查用户权限并限制非特权用户访问 IOCTL 您可以使用bool capable in
  • SSH,运行进程然后忽略输出

    我有一个命令可以使用 SSH 并在 SSH 后运行脚本 该脚本运行一个二进制文件 脚本完成后 我可以输入任意键 本地终端将恢复到正常状态 但是 由于该进程仍在我通过 SSH 连接的计算机中运行 因此任何时候它都会登录到stdout我在本地终
  • 如何使用 JSch 将多行命令输出存储到变量中

    所以 我有一段很好的代码 我很难理解 它允许我向我的服务器发送命令 并获得一行响应 该代码有效 但我想从服务器返回多行 主要类是 JSch jSch new JSch MyUserInfo ui new MyUserInfo String

随机推荐

  • python - write() argument must be str, not bytes

    python write argument must be str not bytes 源代码 import requests r requests get https www baidu com img bd logo1 png if r
  • 【满分】【华为OD机试真题2023B卷 JS】计算误码率

    华为OD2023 B卷 机试题库全覆盖 刷题指南点这里 计算误码率 知识点双指针 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 误码率是最常用的数据通信传输质量指标 它可以理解为 在多少位数据中出现一位差错 移动通信网络中
  • Python进阶语法(一)

    目录 一 Python列表推导式 1 1 使用方法 1 2 列表推导式优点 1 2 1 提升效率 1 2 2 方便转换数据 1 2 3 用于过滤数据 1 3 推广延申 1 3 1 字典推导式 1 3 2 元组推导式 1 3 3 集合推导式
  • Phpstorm好用插件

    IDE Eval Reset 无限次免费刷新30天试用期 插件市场安装 在Settings Preferences gt Plugins 内手动添加第三方插件仓库地址 https plugins zhile io 搜索 IDE Eval R
  • Mybatis 向MySql数据库插入带有日期类型字段的数据

    未验证 我们的实体类里面一个字段的日期类型是util Date 在向数据库插入该实体时会报错 说是 日期哪个字段 Data truncation 所以需要做些更改 在mybatis的MAPPER映射文件中对插入的日期进行相应属性的设置
  • java语言StringUtils工具类

    import com mls util tool codec EncodeUtils import com mls util tool collect ListUtils import org apache commons lang3 St
  • MySQL 故障转移

    在高可用领域 除了通过规范化运维和软硬件优化 提升平均失效时间 MTBF 降低平均恢复时间 MTTR 也非常关键 本文主要讲述的内容是其中的故障转移和故障恢复部分 文章目录 降低平均恢复时间 MTTR 故障探测 I 探测方法 II 探测结果
  • 面试官比较看重简历中的哪些维度,有什么写简历的技巧、建议和总结?

    每日一问 面试官比较看重简历中的哪些维度 有什么写简历的技巧 建议和总结 Datawhale优秀回答者 千夜同学 陶志杰 第一 简历一定要工整 字体格式对奇 字体大小不一 格式错乱 文字乱码的 都懒得看 直接垃圾桶 千万别模版导出 第二 一
  • JS——函数

    文章目录 1 函数 1 1 定义函数 1 2 调用函数 2 变量的作用域 2 1 局部变量 2 2 全局变量 2 3 局部作用域 let 2 4 常量 const 3 方法 1 函数 1 1 定义函数 定义方式一 一旦执行到return代表
  • day6作业

    include
  • 记一次spark streaming+kafka 运行时间不稳定调优历程

    记一次spark streaming kafka 运行时间不稳定调优历程 问题现象 首次使用spark streaming进行流式计算的时候遇到的一个问题 即spark streaming读取kafka消息进行流式计算 但是在数据量比较大的
  • redis查看所有key和value_Redis介绍和数据类型及单机版安装

    Redis 是完全开源免费的 遵守BSD协议 是一个高性能的key value数据库 Redis 与其他 key value 缓存产品有以下三个特点 Redis支持数据的持久化 可以将内存中的数据保存在磁盘中 重启的时候可以再次加载进行使用
  • 36D杯CTF——mengxinstack

    记录一下36D的mengxinstack题目 下载题目 checksec下分析 开启了很多保护 有canary 64位 IDA分析 知道这个程序先执行了一次read 再把read进去的东西print出来 再read 两次read都会造成栈溢
  • (Java)leetcode-1325 Delete Leaves With a Given Value(删除给定值的叶子节点)

    题目描述 给你一棵以 root 为根的二叉树和一个整数 target 请你删除所有值为 target 的 叶子节点 注意 一旦删除值为 target 的叶子节点 它的父节点就可能变成叶子节点 如果新叶子节点的值恰好也是 target 那么这
  • 苹果11文件夹怎么连接服务器,(11)文件服务器

    要求 编写一个 HTTP 文件 服务器 它用于将每次所请求的文件返回给客户端 服务器需要监听所提供的第一个命令行参数所制定的端口 同时 第二个会提供给程序的参数则是所需要响应的文本文件的位置 在这一题中必须使用fs createReadSt
  • 什么是配置环境?如何配置环境?回答很多计算机入门者都会遇到的问题

    什么是配置环境 如何配置环境 回答很多计算机入门者都会遇到的问题 1 前言 编程入门时往往会遇到各种需要配置环境变量的场景 比如刚开始学习python java等 我也和你们一样经历过在网上找各种配环境的教程 然后按部就班地操作 但重复进行
  • 【卡尔曼滤波器】递归算法

    大家好 我是小政 最近在学习卡尔曼滤波 本篇文章记录一下我学习的卡尔曼滤波器中的递归算法 通过举例子让大家更加清晰理解递归到底是什么 希望与同是卡尔曼滤波研究方向的同学进行一些交流 递归算法 1 为什么要用卡尔曼滤波器 2 公式推导 3 举
  • java.lang.OutOfMemoryError: Java heap space .

    java lang OutOfMemoryError Java heap space 解决方案 1 进入eclipse界面中的servers选项卡 右击 工程配置的tomcat项 单击 Open 如图 2 单击 Open launch co
  • c语言课程设计籍贯流程图,学生籍贯信息记录簿(C语言课程设计)教程解读.doc

    计算机科学与技术系 课程设计报告 2012 2013 学年第1学期 课程C语言课程设计课程设计名称学生籍贯信息记录簿设计学生姓名学号专业班级指导教师 目录 一 绪言 需求分析 二 系统设计 一 设计思想 二 源程序设计 三 设计表示 三 系
  • Linux中的线程(一)-- 线程的创建

    什么是线程 Linux 中的线程被称为 轻量级进程 Lightweight Process LWP 它是在进程内部运行的一种 子进程 与传统的进程不同 线程共享相同的虚拟地址空间和其他资源 例如打开的文件 信号处理程序和用户 ID 等 线程