线程-Linux下的轻量级进程

2023-11-08

首先我们知道,每个进程都是在各自独立的地址空间上运行,如果要同时完成好几个任务,比如你一边在下载软件,另一边在进行着其他的操作。那么试想一下,可不可以在一个进程里面把这几个事件同时进行呢,这里就要提到线程的概念了,但其实Linux中,并没有确切的线程的概念,普遍的就是将线程称为是一个轻量级的进程(Lightweight Process,LWP)。

线程简介

线程是在进程的地址空间上运行的,所以同一进程的多个线程之间是共享资源的,但所谓的共享也不是一味的将所有的资源都共享,一般共享以下进程资源:
1、文件描述符表
2、每种信号的处理方式(SIG_IGN,SIG_DFL或者自定义的信息处理函数)
3、当前工作目录
4、用户ID和组ID

但是每个线程都有各自私有维护的一部分资源:
1、线程ID
2、上下文,包括各种寄存器的值,程序计数器和栈指针
3、栈空间
4、errno变量
5、信号屏蔽字
6、调度优先级

总的来说,线程是程序中一个单一的顺序控制流程。是系统独立调度和分派CPU的基本单位(指运行中的程序的调度单位)。如果单个程序中同时运行多个线程完成各自不同的工作,就称为多线程

线程控制

1、线程创建

int pthread_create(pthread_t *thread ,const pthread_attr_t *attr,void *(*start_routine) (void*),void *arg); 

成功返回0,失败返回对应的错误码。这里不同于以往学到一些函数,错误的话都会返回-1,而将错误号保存在全局变量errno中。以为ptheread相关的东西是pthread库提供的,所以这是为了兼容其它函数借口而提供的,所以在用在pthread库的时候,在编译的时候记得要加上-lpthread,否则编译器会提示你函数未定义。
第一个参数就是你要创建的线程号。在 一个线程中调 用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执 行, 而新的线程所执 行的代码由我们传给pthread_create的函数指针start_routine决 定。start_routine函数接收 一个参数,是通过pthread_create的arg参数传递给它的,该参数的类型为 void ,这个指针按什么类型解释由调 用者 自 己定义。start_routine的返回值类型也是void ,这个指针的含义同样由调 用者 自 己定义。start_routine返回时,这个线程就退出了,其它线程 可以调 用pthread_join得到start_routine的返回值,类似于 父进程调 用wait(2)得到 子进程的退出 状态

下面用具体的例子来说明:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

pthread_t tid;//创建一个线程ID

void* thread_run(void* _val)
{
    printf("%s :pid is :%d,tid is :%u\n",(char*)_val,(int)getpid(),(unsigned long long ) pthread_self());

    return NULL;
}

int main()
{
    int err  = pthread_create(&tid,NULL,thread_run,"other thread run");//这里的thread_run就是该线程所以运行的代码,即你自己定义的函数
    if(err != 0)
    {   
        printf("create thread error! info is : %s\n",strerror(err));
        exit(err);
    }
    printf("main thread run : pid is :%d,tid is :%u\n",(int)getpid(),(unsigned long long)pthread_self());
    sleep(1);
    return 0;

}

这里写图片描述
由结果可以看出,在Linux下,pthread_t的类型是一个地址值。而且这里由于pthread_creat的错误码不在errno中保存,所以不能直接用perror()打印错误信息,因此先使用strerror()把错误码转换成错误信息在打印出来。

2、线程终止

void pthread_exit(void *retval);

这里的retval是void *类型的,和线程的函数返回值用方一样。其他线程可以通过调用pthread_join来获得该指针。

若要单一停止掉一个线程而不终止进程的话,有下面几种方法:

1、从线程函数return,不适用于主线程
2、一个线程可以自己调用pthread_cancel终止同一进程的另一个线程
3、线程可以自己调用pthread_exit终止自己

要注意,就是pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其他线程得到这个返回指针时线程函数已经退出了。

3、线程等待

int pthread_join(pthread_t thread,void **retval);

返回值:成功返回0,失败返回错误号

调用该函数的线程将处于挂起状态,一直到ID为pthread的线程终止,而ptherad线程有下面几种终止,所以join也就可以得到几种不同的终止状态。

1、通过return返回,则value_ptr指向单元里存放thread函数返回值
2、被pthread_cancel函数异常终止,value_ptr指向单元里存放常数PTHREAD_ACNCELED
3、自己调用pthread_exit函数,value_ptr指向单元里存放着传给pthread_exit的参数,如果不关心就设置为NULL

下面通过实例再来说明一下这几个函数的用法


ude<stdio.h>
#include<stdlib.h>
#include<pthread.h>


void *thread1(void *_val)
{
    printf("thread 1 returning...\n");
}

void *thread2(void *_val)
{
    printf("thread 2 exiting...\n");
    pthread_exit((void*)2);
}

void *thread3(void *_val)
{
    while(1)
    {
        printf("pthread 3 is runnign,wait for be cancel...\n");
        sleep(1);
    }
    return NULL;
}

int main()
{
    pthread_t tid;
    void * tret;
    //thread 1 return
    pthread_create(&tid,NULL,thread1,NULL);
    pthread_join(tid,&tret);
    printf("thread return,thread id id:%u,return code is %d\n",(unsigned long)tid,(int)tret);
    //thread 2 exit
    pthread_create(&tid,NULL,thread2,NULL);
    pthread_join(tid,&tret);
    printf("thread exit,thread id id:%u,return code is %d\n",(unsigned long)tid,(int)tret);
    //thread 3 cancel by other
    pthread_create(&tid,NULL,thread3,NULL);
    sleep(3);
    pthread_cancel(tid);
    pthread_join(tid,&tret);
    printf("thread return,thread id id:%u,return code is %d\n",(unsigned long)tid,(int)tret);
    return 0;

}

这里写图片描述

可以看到在Linux的pthread库中常数PTHREAD_CANCELED的值是-1,一般情况下一个线程终止后,其终止状态被一直保留到其他线程通过调用pthread_join获取它的状态为止。但是线程是可以被置为detach状态的,这样线程是被分离出去的,即一旦终止了就自行回收自己占用的所有资源,而不会保留自己的状态。那么下面就说一下关于detach函数的东西。

4、线程分离

int pthread_detach(pthread_t thread);

首先,任何一个线程刚被创建出来时,默认是可结合的,为了避免内存泄漏,每一个可结合的线程最后都应该被显示的回收,即调用pthread_join()函数,否则的话就将该进程通过pthread_detach()函数分离出去,而一旦我们对一个已经分离出去的线程进行join的话,会返回EINVAL。

但是由于调用pthread_join后,该线程一直不退出,则调用者会被堵塞,有些情况下我们是不想这样,所以就可以使用pthread_join()函数来解决该问题。因为被分离出去的线程在运行结束后,会自动释放所有的资源,所以也就不需要就等待它。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

void *thread_run(void *_val)
{
    pthread_detach(pthread_self());
    printf("%s\n",(char *)_val);
    return NULL;
}

int main()
{
    pthread_t tid;
    int tret = pthread_create(&tid,NULL,thread_run,"thread1 run...");
    if(tret != 0)
    {
        printf("create thread error!,info is :%s\n",strerror(tret));
        return tret;
    }
    //wait
    int ret = 0;
    sleep(1);
    if(0 == pthread_join(tid,NULL))
    {
        printf("pthread wait success!\n");
        ret = 0;
    }
    else
    {
        printf("pthread wait failed!\n");
        ret = 1;
    }
    return ret;
}

这里写图片描述

可以看出来,只要一个线程一旦被分离出去,就不会再被join接收到。

线程与进程


最后再说几点线程和进程之间的区别:

1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
3)调度和切换:线程上下文切换比进程上下文切换要快得多。
4)在多线程OS中,进程不是一个可执行的实体。

刚开始提到了,要用到pthread库,编译时必须加上 -lthread,我是在Makefile文件里加入的。

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

线程-Linux下的轻量级进程 的相关文章

  • 我应该使用哪个 Linux 发行版作为 Xen 主机? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我为家庭办公室订购了一台服务器 我想用 Xen 对其进行分区 我认为这将使事情保持干净并且更容易维护 我将运行 MySQL PostgreSQL
  • 具有少量父设备属性的 udev 规则

    我需要复杂且通用的udev规则来确定插入任何 USB 集线器的特定端口的 USB 设备 所以 我必须结合设备树不同层的父属性 我有这个 udevadm info query all name dev ttyUSB0 attribute wa
  • 确保 config.h 包含一次

    我有一个库项目 正在使用 Linux 中的 autotools 套件移植到该项目 我对自动工具很陌生 本周 我已经了解了其操作的基础知识 我有一个关于如何保留内容的问题config h免遭重新定义 我惊讶地发现生成的config h文件也没
  • 如何在 Linux 主机上的 docker 容器中挂载目录 [重复]

    这个问题在这里已经有答案了 我想将一个目录从 docker 容器挂载到本地文件系统 该目录是网站根目录 我需要能够使用任何编辑器在本地计算机上编辑它 我知道我可以跑docker run v local path container path
  • Linux 上的 Python 3.6 tkinter 窗口图标错误

    我正在从 Python GUI 编程手册 学习 Python GUI 某项任务要求我通过将以下代码添加到我的配方中来更改窗口图标 Change the main windows icon win iconbitmap r C Python3
  • 如何成功使用RDAP协议代替whois

    我对新的 RDAP 协议有点困惑 也不知道何时进一步追求它有意义 在我看来 每个人都同意它是 whois 的继承者 但他们的数据库似乎是空的 在 ubuntu 上我尝试了 rdapper nicinfo 甚至他们的 RESTful API
  • 如何在我的 AWS EC2 实例上安装特定字体?

    我有一个在 AWS EC2 Amazon Linux Elastic Beanstalk 实例上运行的 Python 应用程序 该实例需要某些特定字体才能生成输出 并且想知道如何在部署或实例启动过程中安装它们 我的代码在本地计算机 OS X
  • git在Windows和Linux之间切换后强制刷新索引

    我有一个Windows和Linux共享的磁盘分区 格式 NTFS 它包含一个 git 存储库 约 6 7 GB 如果我只使用Windows or 只使用Linux操作 git 存储库一切正常 但是每次切换系统的时候git status命令将
  • 为什么在 Linux 上字符串文字的内存地址与其他字符串文字的内存地址如此不同?

    我注意到字符串文字在内存中的地址与其他常量和变量 Linux 操作系统 非常不同 它们有许多前导零 未打印 Example const char h Hi int i 1 printf p n void h printf p n void
  • 无法执行'x86_64-conda_cos6-linux-gnu-gcc':没有这样的文件或目录(pysam安装)

    我正在尝试安装 pysam 执行后 python path to pysam master setup py build 这个错误的产生是 unable to execute x86 64 conda cos6 linux gnu gcc
  • 如何不断刷新屏幕并实时更新[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我想在linux上写一个C程序 不断刷新屏幕并实时更新 例如类似于top终端中的命令 谁能指出我正确的方向 为了保持它跨终端类型的可移
  • 在Linux中断上下文中运行用户线程

    我正在编写一些定制的应用程序 并允许更改 Linux 内核中的中断处理程序代码 我有一个用户线程正在等待中断发生 如果发生中断 那么我要做的第一件事就是执行该用户线程 有什么办法让它发挥作用吗 Thanks 创建一个字符设备 这就是内核所做
  • linux x86 汇编语言 sys_read 调用的第一个参数应为 0 (stdin)

    我正在编写一个简单的汇编程序来从标准输入读取 如 scanf 这是我的代码 section bss num resb 5 section txt global start start mov eax 3 sys read mov ebx 0
  • 适用于 KDE 和 Gnome 的 Gui [重复]

    这个问题在这里已经有答案了 我想为一个现在是 CLI 的应用程序编写一个 gui 它需要在 KDE 和 Gnome DE 中 看起来不错 充分利用用户的外观设置 如果我选择 Qt 或 GTK 我能够做到这一点吗 它们与两个 DE 集成良好吗
  • 如何查找连接到 AF_INET 套接字的客户端的 UID?

    有什么方法或类似的东西ucred for AF UNIX如果是AF INET插座 TCP在我的例子中 找出连接到我的套接字的客户端的UID 还有 proc net tcp但它显示了UID of the creator插座的而不是连接的cli
  • 设置 Apache POI 的路径

    我想创建 Excel 文件并使用 java 程序在该文件中写入数据 That is here http www techbrainwave com p 554我在 java 文件所在的位置提取了 Apache POI 并将该路径包含在路径变
  • Linux shell 脚本中的 while 循环超时

    这工作正常 无限循环 while TRUE do printf done 我在尝试着timeout this while loop与timeout命令 所有这些都不起作用 timeout 5 while TRUE do printf don
  • 没有可用的符号表信息

    我正在测试第三方的库 它崩溃了 当我想查看崩溃的原因时 我的 gdb 告诉我没有可用的调试符号 Program received signal SIGSEGV Segmentation fault Switching to Thread 0
  • 如何使用 sed 仅删除双空行?

    我找到了这个问题和答案 https stackoverflow com questions 4651591 howto use sed to remove only triple empty lines关于如何删除三重空行 但是 我只需要对
  • GCC 和 ld 找不到导出的符号...但它们在那里

    我有一个 C 库和一个 C 应用程序 尝试使用从该库导出的函数和类 该库构建良好 应用程序可以编译 但无法链接 我得到的错误遵循以下形式 app source file cpp text 0x2fdb 对 lib namespace Get

随机推荐

  • Ubuntu14.04安装配置NFS用于挂载嵌入式文件系统

    Ubuntu14 04安装配置NFS用于挂载嵌入式文件系统 1 安装 sudo apt get install nfs kernel server rpcbind 2 配置 vi etc exports 在文件的最后一行加上 yaffs2
  • 获取随机位数阿拉伯数字

    int Math random 9 1 1000 这里是随机4位数 需要几位数 就乘以几个零 int Math random 9 1 100 随机3位数 int Math random 9 1 10 随机2位数 来个方法吧 获取随机位数的阿
  • IPSec 基础介绍

    IPSec是IETF Internet Engineering Task Force 制定的一组开放的网络安全协议 它并不是一个单独的协议 而是一系列为IP网络提供安全性的协议和服务的集合 包括认证头AH Authentication He
  • python TimedRotatingFileHandler 配置参数 (转)

    TimedRotatingFileHandler这个模块是满足文件名按时间自动更换的需求 这样就可以保证日志单个文件不会太大 用法很简单 示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 impo
  • python学习之【模块】

    前言 上一篇文章 python学习之 深拷贝 中学习了python中的深浅拷贝学习内容 这篇文章接着学习python中的模块 什么是模块 在python中 一个文件 以 py 为后缀名的文件 就叫做一个模块 每一个模块在python里都被看
  • 群晖做网页服务器_群晖NAS软件DS get介绍及使用方法教程

    我的NAS介绍第二篇 群晖NAS软件介绍与应用之DS get篇前言 1 为什么选择NAS之所以我现在建议大家选择NAS 不仅仅因为网盘的不稳定性和不安全性 遇到和谐大神不说 网盘也经历了各种风风雨雨 从和谐到倒闭不过一步之遥 大家都懂的 还
  • Mysql-连接https域名的Mysql数据源踩的坑

    背景介绍 大家在实际项目中 大部分都会用到关系数据库mysql 通常数据库服务器提供的都是ip的方式 所以不会出现本文涉及到的https域名的问题 本文介绍的是基于数据库服务器是分配了指定域名且有ssl证书的https 连接数据源 遇到的问
  • java面向对象基础练习--实现简单的图书管理系统

    这个系统使用的是java的基础语法 没有使用数据库 实现图书管理系统基础的查询 增加 删除 借阅 归还 打印 退出功能 这个小项目作为我java基础语法的综合运用 主要是为了建立面向对象编程的思想 培养编程习惯 如果有错误或者更好的实现方法
  • 深入详解ThreadLocal

    本文已收录至GitHub 推荐阅读 Java随想录 微信公众号 Java随想录 原创不易 注重版权 转载请注明原作者和原文链接 文章目录 什么是ThreadLocal ThreadLocal 原理 set方法 get方法 remove方法
  • 又回来了

    又回来了 一年多没有来了 再次回来还是感觉那么熟悉 那么亲切 怀念以前在学校的日子 怀念苦苦思索技术的日子 除了学习没有繁杂的社会关系要处理 单纯快乐着 为了一个小小的技术难题 愿意不吃不喝去摸索 去测试 在成功的那一刻忘记了一切疲倦和劳累
  • c++ 实现数据库连接池

    c 实现数据库连接池 自己尝试用c 新标准实现了数据库连接池 代码简化了很多 思路 将数据库的连接当作一个对象添加进list队列中 在连接池创建的时候就建立好队列 并添加自定义大小的连接对象 连接对象用智能指针来管理 现代c 中不应该出现d
  • Api接口版本管理实现

    Api接口版本管理实现 引言 实现 RequestCondition 实现代码 ApiVersion注解 ApiVersionRequestCondition 版本匹配 ApiVersionHandlerMapping 将condition
  • fastjson对泛型的反序列化

    文章目录 具体告警分析 告警影响 fastjson未指定泛型具体类型 fastjson TypeReference指定泛型具体类型 可以看到fastjson反序列化时IDEA提示告警 Unchecked assignment 怎么解决这个告
  • ubuntu+vscode构建c++开发调试环境

    1 vscode下载与安装 下载 Visual Studio Code Mac Linux Windows下载deb文件 运行指令安装vscode sudo dpkg i xxx deb 如果报 dpkg 错误 另外一个进程已经为 dpkg
  • python 3.7版本 打不开 python 3.8 保存的pickle文件

    1 问题描述 最近有一个pickle文件 当我使用python3 7 读取的时候报错 ValueError unsupported pickle protocol 5 查找原因发现是原始文件是用高版本的python解释器 比如3 8 保存的
  • 数据结构实验--表达式的后缀表示

    一 问题描述 表达式中包含运算对象 运算符和圆括号等 习惯上使用中缀表示 指运算符夹在两运算符对象中间 形式 计算表达式的值 涉及到运算符的优先级别 如先乘除后加减 括在一对圆括号中的子表达式必须先计算 因此 圆括号可视为特殊的运算符 具有
  • Linux--遇见的一些小错误

    一 问题 linux下运行出现 sh 1 pause not found 原因 在windows系统下 使用此语句 但linux不认识 system pause 解决方法 删去system pause 二 问题 编程时出现 Warning
  • Unity3D——AR小游戏

    文章目录 有趣的AR小游戏制作 环境准备 具体实现 替换Camera 上传识别卡 下载识别卡模型 编辑游戏对象 实验环境是否配置成功 导入Lean Touch脚本 制作成Android应用 游戏制作 实验结果 有趣的AR小游戏制作 环境准备
  • MySQL 5.1中文参考手册 - 学习笔记

    MySQL 5 1中文参考手册地址 http dev mysql com doc refman 5 1 zh index html 学习笔记及重要点 由于最近使用MySQL数据库的机会越来越多 所以看来这次要认真的学习一下了 以往只懂得皮毛
  • 线程-Linux下的轻量级进程

    首先我们知道 每个进程都是在各自独立的地址空间上运行 如果要同时完成好几个任务 比如你一边在下载软件 另一边在进行着其他的操作 那么试想一下 可不可以在一个进程里面把这几个事件同时进行呢 这里就要提到线程的概念了 但其实Linux中 并没有