【Linux多线程】认识多线程创建线程

news2024/12/23 1:31:15

文章目录

  • 什么是多线程
    • 为什么称linux下的线程是轻量级进程呢?
  • 线程的优点
  • 线程的缺点
  • 线程异常
  • 线程和进程
  • 创建线程
    • 1.pthread_create
    • 2.pthread_self

什么是多线程

进程是正在运行的程序的实例,而线程(thread)是进程中的一个执行路线。一个进程可以拥有多个线程。从程序的角度上来说,线程是一个独立运行程序的片段。当程序运行时,进程把大部分资源合理分配给每个执行流(线程),且所有线程共享进程的地址空间,所以线程实际上就是一个轻量级的进程。下面给出进程中的线程示意图:


值得注意的是,windos下的线程是有线程控制块(TCB)的。而linux下的进程控制块和线程控制块都是task_struct

为什么称linux下的线程是轻量级进程呢?

这是因为linux内核并没有单独为线程设计一套管理方案,而是通过相同的机制来管理进程和线程,只不过线程拥有的资源是进程的一部分,所以称linux下的线程是轻量级进程。并且,Linux内核中的调度器并不区分线程和进程,可以是单线程的进程,也可以只是一个线程。为了让用户使用起来区分线程和进程,linux向上(用户态)提供了POSI标准的线程接口(如pthread库),在内核用clone系统调用创建和管理线程。尽管有线程这个模型,但底层还是轻量级的进程

总结:==线程是共享同一进程的地址空间和资源的执行单元=。

线程的优点

  1. 共享资源:同一进程内的线程共享地址空间,能访问相同的全局变量,堆和文件描述符等。这种共享使得线程间通信变得更加高效。
  2. 独立的执行流:每个线程都有自己的程序计数器、寄存器和栈,这使得线程可以独立执行。线程的独立性使得多个线程并行执行多个任务,提高了此程序的响应性和吞吐量。
  3. 轻量级:相比于进程,线程的创建开销会小很多,且不需要分配独立的地址空间。上下文切换也比进程快,因为不涉及地址空间的切换
    4.并发执行:在多核处理器上,不同的线程可以做到真正的并行执行

线程的缺点

  1. 同步复杂性:由于共享进程空间,多个线程同时访问和修改共享数据时可能会导致数据不一致等问题
  2. 性能损失:使用锁和其它同步机制会导致性能下降
  3. 调试难度提高:编写和调试一个多线程程序要比单线程程序困难得多

线程异常

  • 单个线程如果出现除0或者访问野指针等问题导致线程崩溃,进程也会随着崩溃
  • 进程终止,该进程的所有线程都会终止。这也就意味着,如果某一个线程出了异常进而终止进程的话,其它的线程也都会被终止。这也是线程不安全的原因之一。

线程和进程

  • 进程是资源分配的基本单位,而线程是调度的基本单位
  • 具体来说,线程共享以下进程资源:
    • 代码段和数据段
    • 文件描述符表
    • 每种信号的处理方式即handler表
    • 环境变量包括当前工作目录
    • 用户id和组id
  • 虽然线程共享进程的数据,但有属于自己的一些数据:
    • 线程ID
    • 一组寄存器
    • errno错误流
    • 信号屏蔽字
    • 调度优先级

进程和线程的关系如下图:
在这里插入图片描述

创建线程

在linux中,通常使用POSIX线程库,即pthread库。pthread库提供了一组用于线程创建、管理和同步的函数,这些函数被包含在pthread.h头文件中。pthread库的主要包含了线程管理、线程同步、线程属性相关的函数,下面介绍线程管理中的一些常用函数。

1.pthread_create

功能:创建一个新的线程
原型:

#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变量的一个指针,用于存储创建线程的标识符ID(输出型参数)
  • pthread_attr_t类型是一个线程属性的类,该类定义了线程的所有属性,包括分离状态、栈的大小等
  • attr是一个指向const pthread_attr_t对象的指针,用于初始化被创建线程的属性。如果设置为NULL,则使用默认属性
  • start_rountine是一个函数指针,该函数的参数和返回值类型都是void*。表示线程线程执行的函数
  • arg是传递给线程函数的参数。可以是NULL,如果需要传递多个参数,可以将其打包成结构体类型对象传进去。同样如果想返回多个值,可以将值打包成一个结构体再返回。
  • 创建成功返回0。失败则返回一个非0值,表示错误代码。常见得到错误码有:
    • EAGAIN:系统资源不足,无法创建更多线程。
    • EINVAL:无效的线程属性
    • EPERM:没有足够的权限设置线程属性

给出代码样例,演示使用pthread_create创建线程:

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

using namespace std;

void *rout(void *arg)//线程执行函数
{
    while (true)
    {
        cout << "i am thread num: " << *(int *)arg << endl;
        sleep(1);
    }
    return NULL;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    if (res != 0)
    {//错误码检查
        fprintf(stderr, "pthread_create: %s\n", strerror(res));
        exit(1);
    }
    while (true)
    {
        cout << "I am main thread" << endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
这样我们就成功的使用pthread_create函数创建了一个线程。值得注意的是,执行main函数的线程我们称为主线程。此外,一个线程可以使用pthread_self函数来获取自己的线程ID.

2.pthread_self

功能:获得当前线程的ID
函数原型:

pthread_t pthread_self(void);

于是我们可以将前面的代码样例改一下,观察结果线程ID:

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

using namespace std;

void *rout(void *arg)
{
    while (true)
    {
        cout << "thread id : " << pthread_self() << " i am thread num: " << *(int *)arg << endl;
        sleep(1);
    }
    return NULL;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    if (res != 0)
    {
        fprintf(stderr, "pthread_create: %s\n", strerror(res));
        exit(1);
    }
    while (true)
    {
        cout << "tid: " << tid << "  I am main thread" << endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
我i们可以观察到,线程ID是一个非常复杂的数字,这个具体数值通常是由线程库内部实现的,可能会使用内存地址等机制来生成唯一的线程ID。

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

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

相关文章

MySQL——JDBC编程

目录 前言 一、JDBC概述 二、准备工作 1.下载MySQL的JDBC驱动包 2.把jar引入到项目中 三、JDBC编程 1.插入操作 2.查询操作 尾声 前言 本篇文章主要介绍如何利用Java代码进行操作数据库&#xff0c;在实际开发中&#xff0c;绝大多数对数据库的操作我们都是通过代码进行…

三十五岁零基础转行成为AI大模型开发者怎么样呢?

以下从3个方面帮大家分析&#xff1a; 35岁转行会不会太晚&#xff1f;零基础学习AI大模型开发能不能学会&#xff1f;AI大模型开发行业前景如何&#xff0c;学完后能不能找到好工作&#xff1f; 一、35岁转行会不会太晚&#xff1f; 35岁正处于人生的黄金时期&#xff0c;拥…

【前端篇】前端开发大厂面试真题

为助力小伙伴们梳理前端知识体系&#xff0c;从而能够充分地做好面试准备&#xff0c;那么今天就来给大家分享一份前端开发的面试真题与相关知识点&#xff0c;其中涵盖了最新版本的八股文&#xff08;包含最新的 Vue 3 面试题&#xff09;、高频算法题以及大佬的面经&#xff…

相对位姿估计

相对位姿估计 示意图 理论推导 离线数据库&#xff1a; P的位置 P [ X , Y , Z ] T P[X,Y,Z]^{T} P[X,Y,Z]T 相机内参 k 1 k_{1} k1​ 安卓手机&#xff1a; 相机内参 k 2 k_{2} k2​ 两个像素点位置 &#xff1a; p 1 和 p 2 p_1和p_2 p1​和p2​ 公式一&#xff1a;…

java版本知识服务系统-高效知识付费Saaas平台的架构与功能模块设计

知识付费平台&#xff0c;作为我国近年来崭露头角的新型在线教育模式&#xff0c;正迅速改变着传统的学习方式。这种模式紧贴用户需求&#xff0c;将高质量的内容作为核心&#xff0c;借助互联网技术的力量&#xff0c;为用户打造了一个轻松便捷的学习环境。这些平台如同知识的…

【3.vi编辑器使用(上)】

一、vi编辑器的三种模式及切换命令 1、vi是linux中最基本的编辑器。但vi编辑器在系统管理、服务器配置工作中永远都是无可替代的。 2、vi编辑器的三种模式&#xff1a;命令行模式、插入模式、底行模式。 &#xff08;1&#xff09;命令行模式&#xff1a;用户在用vi编辑文件…

同时执行多个python脚本扫描,报如下错误,原因为文件越大读取到内存占用内存越多。

killed nohup python $file unable to fork process cannot allocate memory ls: error while loading shared libraries: libdl.so.2 failed to map segment from shared object cannot allocate memory python进程被系统或者某个用户通过 kill 命令强制终止了

vue3+electron+typescript 项目安装、打包、多平台踩坑记录

环境说明 这里的测试如果没有其他特别说明的&#xff0c;就是在win10/i7环境&#xff0c;64位&#xff0c;node 版本v16.20.2 创建项目 vite官方是直接支持创建electron项目的&#xff0c;所以&#xff0c;这里就简单很多了。我们已经不需要向开始那样自己去慢慢搭建 yarn …

【Linux】centos7编写C语言程序,补充:使用yum安装软件包组

确保已安装gcc编译器 C语言程序&#xff0c;一般使用gcc进行编译&#xff0c;需确保已安装gcc。 若没有&#xff0c;可以使用yum安装gcc&#xff08;版本4.8.5&#xff09;&#xff0c;也可以使用SCL源安装gcc&#xff08;例如&#xff1a;版本9.3&#xff09;。 安装gcc&am…

SpringMVC: 跨线程获取requests请求对象

文章目录 引言I RequestContextHolder1.1 用法1.2 跨线程获取requests请求对象II ThreadLocal2.1 采用threadLocal存储token信息,异步线程无法获取。2.2 InheritableThreadLocal解决异步线程,无法获取token信息问题。引言 异步操作的场景: 不影响业务主流程的操作(日志的记…

外企如何有效面对日益严格的跨境数据传输法律?

在当今这个数据驱动的时代&#xff0c;随着全球化步伐的加快&#xff0c;企业跨国界的数据交流已成为常态。但随之而来的&#xff0c;是各国政府对跨境数据传输日益严格的规定和监管&#xff0c;这让众多外资企业&#xff08;简称“外企”&#xff09;在享受全球市场红利的同时…

Java用反射reflect来实例化对象: class.getDeclaredConstructor().newInstance()

Java用反射reflect来实例化对象: class.getDeclaredConstructor().newInstance() 从java9开始, class.newInstance()已过时, 被加上Deprecated强烈反对注解 SuppressWarnings("removal")CallerSensitiveDeprecated(since"9")public T newInstance()throws …

探索RS与AES加密技术:从经典到现代

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、MD5加密技术&#xff1a;经典中的经典 二、非对称加密&#xff1a;RSA技术的魅力 RSA技…

运维开发.MySQL.范式与反范式化

运维开发 MySQL.三大范式 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/artic…

轻松放大图片600%,Topaz Gigapixel AI图片无损清晰放大软件下载安装

Topaz Gigapixel AI 该软件拥有卓越的性能和先进的技术&#xff0c;能够轻松实现图像的精细放大&#xff0c;最多可将图像放大至原始尺寸的六倍&#xff0c;而无需担心图像质量的损失。 相较于传统的图像放大软件&#xff0c;Topaz Gigapixel AI 表现出了明显的优势。传统软件…

学习笔记——动态路由协议——OSPF(简介)

一、 OSPF简介 1、前言 由于静态路由由网络管理员手工配置&#xff0c;因此当网络发生变化时&#xff0c;静态路由需要手动调整&#xff0c;这制约了静态路由在现网大规模的应用。 动态路由协议因其灵活性高、可靠性好、易于扩展等特点被广泛应用于现网。在动态路由协议之中…

Python保存为json中文Unicode乱码解决json.dump()

保存为json中文Unicode乱码&#xff1a; 可以看到&#xff0c;中文字符没有乱码&#xff0c;只是出现了反斜杠&#xff0c;此时解决方法应考虑是否进行了二次序列化。 一、原因1 在dump时加入ensure_asciiFalse 即可解决&#xff0c;即json.dump(json_data, f, indent4, en…

VSCode小技巧,忽略不想格式化的代码行

零&#xff0e;格式化工具文档 1 . Black Ignoring sections功能 2 . autopep8 disabling-line-by-line功能&#xff1b;&#xff1b;–line-range选项 3 . Prettier prettier-ignore功能(例&#xff1a;适用于JS的// prettier-ignore&#xff0c;适用于CSS的/* prettier-igno…

【WEB前端2024】开源智体世界:乔布斯3D纪念馆-第29课-会员制展厅

【WEB前端2024】开源智体世界&#xff1a;乔布斯3D纪念馆-第29课-会员制展厅 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…

前端Vue小兔鲜儿电商项目实战Day02

一、Pinia快速入门 此处见&#xff1a;Vue从入门到实战Day12-CSDN博客 二、创建项目并精细化配置 1. 创建项目 2. src目录调整 ①删除一些初始化的默认文件 清空assets、components、store、views文件夹下的内容&#xff1b; ②修改剩余代码内容 router/index.js import …