Linux系统编程——线程(1)

目录

  • 线程概要
    • Linux内核线程实现原理
    • 线程的共享/不共享资源
    • 线程优缺点
  • 线程控制原语
    • pthread_self
    • pthread_create
    • pthread_exit
    • pthread_join
    • pthread_cancel
    • 终止线程方式
    • 控制原语对比

前情提要: Linux用户级线程和内核级线程区别

线程概要

Linux内核线程实现原理

类Unix系统中,早期是没有“线程”概念的,80年代才引入,借助进程机制实现出了线程的概念。因此在这类系统中,进程和线程关系密切。

  1. 轻量级进程(light-weight process),也有PCB,创建线程使用的底层函数和进程一样,都是clone

  2. 从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表是相同的

  3. 进程可以蜕变成线程

  4. 线程可看做寄存器和栈的集合

  5. 在linux下,线程最是小的执行单位;进程是最小的分配资源单位

查看线程命令:ps -elf|grep thread

1556722663332

三级映射:进程PCB --> 页目录(可看成数组,首地址位于PCB中) --> 页表 --> 物理页面 --> 内存单元


线程的共享/不共享资源

线程共享资源线程不共享资源
文件描述符表线程id
每种信号的处理方式处理器现场和栈指针(内核栈)
当前工作目录独立的栈空间(用户空间栈)
用户ID和组IDerrno变量
内存地址空间(.text/.data/.bss/heap/共享库)信号屏蔽字
调度优先级

线程优缺点

优点: 1. 提高程序并发性 2. 开销小 3. 数据通信、共享数据方便

缺点: 1. 库函数,不稳定 2. 调试、编写困难、gdb不支持 3. 对信号支持不好

线程控制原语


pthread_self

获取线程ID。其作用对应进程中 getpid() 函数。

​ pthread_t pthread_self(void); 返回值:成功:0; 失败:无!

​ 线程ID:pthread_t类型,本质:在Linux下为无符号整数(%lu),其他系统中可能是结构体实现

​ 线程ID是进程内部,识别标志。(两个进程间,线程ID允许相同)


pthread_create

创建一个新线程。 其作用,对应进程中fork() 函数。

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

​ 返回值:成功:0; 失败:错误号 -----Linux环境下,所有线程特点,失败均直接返回错误号。

参数:

​ pthread_t:当前Linux中可理解为:typedef unsigned long int pthread_t;

参数1:传出参数,保存系统为我们分配好的线程ID

​ 参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。

​ 参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。

​ 参数4:线程主函数执行期间所使用的参数。

练习:创建一个新线程,打印线程ID。注意:链接线程库 -lpthread

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void *tfn(void *arg)
{printf("I'm thread, Thread_ID = %lu\n", pthread_self());return NULL;
}int main(void)
{pthread_t tid;pthread_create(&tid, NULL, tfn, NULL);sleep(1);printf("I am main, my pid = %d\n", getpid());return 0;
}

线程默认共享数据段、代码段等地址空间,常用的是全局变量,或者传参形式。而进程不共享全局变量,只能借助mmap。

全局变量:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>int var = 100;void *tfn(void *arg)
{var = 200;printf("thread\n");return NULL;
}int main(void)
{printf("At first var = %d\n", var);pthread_t tid;pthread_create(&tid, NULL, tfn, NULL);sleep(1);printf("after pthread_create, var = %d\n", var);return 0;
}

1556723666858

传参:

#include <func.h>void *tfn(void *arg){int* var = (int*)arg;*var = 200;printf("thread\n");return NULL;
}int main()
{int var = 100;printf("At first var = %d\n", var);pthread_t tid;pthread_create(&tid, NULL, tfn, &var);sleep(1);printf("after pthread_create, var = %d\n", var);return 0;
}

1556723909222


pthread_exit

作用:将单个线程退出

​ void pthread_exit(void *retval); 参数:retval表示线程退出状态,通常传NULL

线程中,**禁止使用exit函数,会导致进程内所有线程全部退出**。所以,多线程环境中,应尽量少用,或者不使用exit函数,取而代之使用pthread_exit函数,将单个线程退出。任何线程里exit导致进程退出,其他线程未工作结束,主控线程退出时不能return或exit。

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


pthread_join

阻塞等待线程退出,获取线程退出状态 其作用,对应进程中 waitpid() 函数。

​ int pthread_join(pthread_t thread, void **retval); 成功:0;失败:错误号

​ 参数:thread:线程ID (【注意】:不是指针);retval:存储线程结束状态。

​ 对比记忆:

​ 进程中:main返回值、exit参数-->int;等待子进程结束 wait 函数参数-->int *

​ 线程中:线程主函数返回值、pthread_exit-->void *;等待线程结束 pthread_join 函数参数-->void **

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

  1. 如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值。

  2. 如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED。

  3. 如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数。

  4. 如果对thread线程的终止状态不感兴趣,可以传NULL给retval参数。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>typedef struct {int a;int b;
} exit_t;void *tfn(void *arg)
{exit_t *ret;ret = malloc(sizeof(exit_t)); ret->a = 100;ret->b = 300;pthread_exit((void *)ret);
}int main(void)
{pthread_t tid;exit_t *retval;pthread_create(&tid, NULL, tfn, NULL);/*调用pthread_join可以获取线程的退出状态*/pthread_join(tid, (void **)&retval);      //wait(&status);printf("a = %d, b = %d \n", retval->a, retval->b);return 0;
}

1556724875987


pthread_cancel

杀死(取消)线程 其作用,对应进程中 kill() 函数。

​ int pthread_cancel(pthread_t thread); 成功:0;失败:错误号

​ 【注意】:线程的取消并不是实时的,而有一定的延时。需要等待线程到达某个取消点(检查点)。

​ 类似于玩游戏存档,必须到达指定的场所(存档点,如:客栈、仓库、城里等)才能存储进度。杀死线程也不是立刻就能完成,必须要到达取消点。

​ 取消点:是线程检查是否被取消,并按请求进行动作的一个位置。通常是一些系统调用creat,open,pause,close,read,write..... 执行命令man 7 pthreads可以查看具备这些取消点的系统调用列表。也可参阅 APUE.12.7 取消选项小节。

可粗略认为一个系统调用(进入内核)即为一个取消点。如线程中没有取消点,可以通过调用pthreestcancel函数自行设置一个取消点。

被取消的线程, 退出值定义在Linux的pthread库中。常数PTHREAD_CANCELED的值是-1。可在头文件pthread.h中找到它的定义:#define PTHREAD_CANCELED ((void *) -1)。因此当我们对一个已经被取消的线程使用pthread_join回收时,得到的返回值为-1。

终止线程的三种方法。注意“取消点”的概念。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>void *tfn1(void *arg)
{printf("thread 1 returning\n");return (void *)111; 
}void *tfn2(void *arg)
{printf("thread 2 exiting\n");pthread_exit((void *)222);
}void *tfn3(void *arg)
{while (1) {//printf("thread 3: I'm going to die in 3 seconds ...\n");//sleep(1);pthread_testcancel();   //自己添加取消点*/}return (void *)666;
}int main(void)
{pthread_t tid;void *tret = NULL;pthread_create(&tid, NULL, tfn1, NULL);pthread_join(tid, &tret);printf("thread 1 exit code = %d\n\n", (int)tret);pthread_create(&tid, NULL, tfn2, NULL);pthread_join(tid, &tret);printf("thread 2 exit code = %d\n\n", (int)tret);pthread_create(&tid, NULL, tfn3, NULL);sleep(3);pthread_cancel(tid);pthread_join(tid, &tret);printf("thread 3 exit code = %d\n", (int)tret);return 0;
}

1556725457481

终止线程方式

总结:终止某个线程而不终止整个进程,有三种方法:

  1. 从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。

  2. 一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

  3. 线程可以调用pthread_exit终止自己。


控制原语对比

​ 进程 线程

​ fork pthread_create

​ exit pthread_exit

​ wait pthread_join

​ kill pthread_cancel

​ getpid pthread_self 命名空间

转载于:https://www.cnblogs.com/Mered1th/p/10801287.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TCP/IP(二):IP协议

IP协议处于OSI参考模型的第三层——网络层&#xff0c;网络层的主要作用是实现终端节点间的通信。IP协议是网络层的一个重要协议&#xff0c;网络层中还有ARP(获取MAC地址)和ICMP协议(数据发送异常通知) 数据链路层的作用在于实现同一种数据链路下的包传递&#xff0c;而网络层…

Ajax — 大事件项目(第四天)

分类管理 添加分类 初步使用弹出层 给 “添加分类” 绑定一个单击事件单击事件中&#xff0c;使用 layer.open() 实现一个弹出层 type: 1, 弹层的类型是页面层title, “添加文字分类”content: ‘字符串&#xff0c;DOM’,area: [‘500px’, ‘250px’] // ---------------…

redis学习(四)

一、Redis 键(key) 1、Redis 键命令用于管理 redis 的键。 2、Redis 键命令的基本语法如下&#xff1a;redis 127.0.0.1:6379> COMMAND KEY_NAME 3、常用key命令 keys * 获取所有的keyselect 0 选择第一个库move myString 1 将当前的数据库key移动到某个…

TCP/IP(三):IP协议相关技术

在前两篇文章中&#xff0c;我分别介绍了数据链路层和网络层的IP协议。虽然这个系列教程的重点是搞定 TCP/IP&#xff0c;不过不用着急&#xff0c;本文简要介绍完与 IP 协议相关的技术&#xff0c;下一篇文章就会正式、详细的介绍 传输层与 TCP 协议。这篇文章会介绍 DNS、ARP…

Node — 第一天

Node-01 会 JavaScript&#xff0c;就能学会 Node.js&#xff01;&#xff01;&#xff01; **Node.js 的官网地址&#xff1a; ** Node.js 的学习路径&#xff1a; JavaScript 基础语法 Node.js 内置 API 模块&#xff08;fs、path、http等&#xff09; 第三方 API 模块&…

TCP/IP(四):TCP 与 UDP 协议简介

从本章开始&#xff0c;我们开始介绍最重要的传输层。传输层位于 OSI 七层模型的第四层&#xff08;由下往上&#xff09;。顾名思义&#xff0c;传输层的主要作用是实现应用程序之间的通信。网络层主要是保证不同数据链路下数据的可达性&#xff0c;至于如何传输数据则是由传输…

Node — 第二天

http模块 搭建服务器的步骤 ① 导入 http 模块 ② 创建 web 服务器实例 ③ 为服务器实例绑定 request 事件&#xff0c;监听客户端的请求 ④ 启动服务器 // ① 导入 http 模块 const http require(http);// ② 创建 web 服务器实例 const server http.createServer();/…

TCP/IP(五):TCP 协议详解

上一节 中讲过&#xff0c;TCP 协议是面向有连接的协议&#xff0c;它具有丢包重发和流量控制的功能&#xff0c;这是它区别于 UDP 协议最大的特点。本文就主要讨论这两个功能。 数据包重发 数据发送 丢包重发的前提是发送方能够知道接收方是否成功的接收了消息。所以&#…

TCP/IP(六):HTTP 与 HTTPS 简介

本文是准备面试过程中网络部分总结整理的最后一篇文章&#xff0c;主要介绍以下知识&#xff1a; HTTP 协议概述POST 请求和 GET 请求Cookie 和 Session数据传输时的加密HTTPS 简介 HTTP 协议 在 OSI 七层模型中&#xff0c;HTTP 协议位于最顶层的应用层中。通过浏览器访问网…

Node — 第三天

模块化 什么是模块化 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层把系统划分成若干模块的过程。 对于整个系统来说&#xff0c;模块是可组合、分解和更换的单元。 生活中的模块化 编程中的模块化 编程领域中的模块化&#xff0c;就是遵守固定的规则&#xff0c;…

FireDAC 中文字段过滤问题

当使用 FireDAC Filter 过滤数据的时候&#xff0c;通常这样写&#xff1a; FDMemTable.Filtered : False; FDMemTable1.Filter : 姓名 string(edtFilter.Text).QuotedString; FDMemTable.Filtered : True; 将会报错&#xff1a;[FireDAC][Stan][Eval]-107. Invalid characte…

Express — 使用步骤

Express Express 介绍安装搭建服务器的步骤express提供的新方法GET接口 获取GET中的请求参数 POST接口 获取POST请求提交的数据 中间件原理中间件语法中间件初体验&#xff08;设置响应头&#xff09;中间件开放静态资源中间件接收POST请求体中间件返回404页面 express 介绍 E…

大模型工具_Langchain-Chatchat

https://github.com/chatchat-space/Langchain-Chatchat 原Langchain-ChatGLM 1 功能 整体功能&#xff0c;想解决什么问题 基于 Langchain 与 ChatGLM 等LLM模型&#xff0c;搭建一套针对中文场景与开源模型&#xff0c;界面友好、可离线运行的知识库问答解决方案。 当前解决…

iOS 键盘风格详解UIKeyboardType

一、键盘风格 UIKit框架支持8种风格键盘。 [java] view plaincopy print?typedef enum { UIKeyboardTypeDefault, // 默认键盘&#xff1a;支持所有字符 UIKeyboardTypeASCIICapable, // 支持ASCII的默认键盘 UIKeyboardTypeNu…

Node — 第四天(Promise与路由)

Promise - ES6新对象 Promise能够处理异步程序。 回调地狱 JS中或node中&#xff0c;都大量的使用了回调函数进行异步操作&#xff0c;而异步操作什么时候返回结果是不可控的&#xff0c;如果我们希望几个异步请求按照顺序来执行&#xff0c;那么就需要将这些异步操作嵌套起来…

winform datagridview控件使用

最近做项目时&#xff0c;显示查询结果总需要绑定到datagridview控件上显示&#xff0c;总结了给datagridview绑定数据的方式&#xff0c;以及导出datagridview数据到excel表格&#xff0c;如有错误请多指教 1.直接绑定数据源&#xff0c;可以绑定的数据格式有List<T>,Da…

Node — 第五天

1. MySQL数据库 phpstudy 数据库服务器及图形化软件 软件链接 链接&#xff1a;https://pan.baidu.com/s/1F8wdoMstHAJkINfDKDejsw 提取码&#xff1a;xl3k 数据库对于我们前端同学来说&#xff0c;就是一个了解。 对于不会变化的数据&#xff08;省、市、县&#xff09;&…

iOS10 权限访问崩溃

手机升级了 iOS10 Beta&#xff0c;然后用正在开发的项目 装了个ipa包&#xff0c;发现点击有关 权限访问 直接Crash了&#xff0c;并在控制台输出了一些信息&#xff1a; This app has crashed because it attempted to access privacy-sensitive data without a usage descr…

Node — 第六天(前后端分离)及(身份验证)

综合应用服务端知识点搭建项目 下载安装所需的第三方模块 npm init -y npm i express cors mysql # express 用于搭建服务器 # cors 用于解决跨域 # mysql 用于操作数据库# 后面用到什么&#xff0c;再下载创建app.js 之前&#xff0c;我们开启一个服务器&#xff0c;js文件…

继承上机作业

1、实现如下类之间的继承关系&#xff0c;并编写Music类来测试这些类 2、编写一个Java应用程序&#xff0c;该程序包括3个类&#xff1a;Monkey类、People类和主类E。要求&#xff1a; (1) Monkey类中有个构造方法&#xff1a;Monkey (String s)&#xff0c;并且有个public voi…