[C++/Linux] Linux线程详解

目录

 

一.什么是线程?

并发(Concurrency)

并行(Parallelism)

  1.1 线程的概念

  1.2 线程的基本函数

 1.3 线程的基本使用例子:

 二.线程的属性

2.1线程属性使用例子

三.线程互斥

 3.1互斥锁

3.2互斥锁常用函数 

 3.3互斥锁使用例子


一.什么是线程?

    在Linux中,线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。同一进程中的多条线程将共享该进程所拥有的全部资源,但每条线程都有各自的调用栈和程序计数器,能够独立运行。

   那么什么是并发与并行呢?        

并发(Concurrency)

并发是指系统能够处理多个同时发生的任务。在并发编程中,多个任务在同一时间段内执行,但它们可能不是同时执行。并发可以通过时间分片(time slicing)来实现,操作系统快速地在多个任务之间切换,给人一种同时执行的错觉。

并发的主要目的是提高资源的利用率和系统的响应速度。例如,在一个Web服务器中,可以同时处理多个用户的请求,这样就可以更有效地利用服务器的资源,并缩短用户的等待时间。

并行(Parallelism)

并行是指系统能够在同一时刻执行多个任务。并行编程需要多个处理器或者多个核心,每个处理器或核心可以独立地执行一个任务。并行是真正的“同时”执行,它通过增加计算资源来提高性能。

并行的主要目的是通过同时执行多个任务来提高性能和缩短任务的完成时间。例如,在一个多核处理器上,可以同时运行多个线程或者进程,这样可以显著减少执行复杂计算或者数据处理任务所需的时间。

  1.1 线程的概念

  • 轻量级进程(Lightweight Process, LWP):线程又被称为轻量级进程,因为线程的创建、撤销和切换比进程更快。
  • 线程的属性:线程具有就绪、阻塞和运行三种基本状态,以及创建和终止两种辅助状态。
  • 线程的执行:线程可以并发执行,这意味着它们似乎是在同时运行,但实际上是在CPU时间片轮转机制下快速切换执行。

如何使用线程:

在Linux中,线程可以通过 POSIX 线程(pthread)库来使用。这个库提供了一系列函数来创建、同步和管理线程。

线程共享资源

1.文件描述符表
2.每种信号的处理方式
3.当前工作目录
4.用户ID和组ID
5.内存地址空间 (.text/.data/.bss/heap/共享库)


线程非共享资源

1.线程id
2.处理器现场和栈指针(内核栈)
3.独立的栈空间(用户空间栈)
4.errno变量
5.信号屏蔽字
6.调度优先级

线程优、缺点

  • 优点: 1. 提高程序并发性 2. 开销小 3. 数据通信、共享数据方便
  • 缺点: 1. 库函数,不稳定 2. 调试、编写困难、gdb不支持 3. 对信号支持不好
  • 优点相对突出,缺点均不是硬伤。Linux下由于实现方法导致进程、线程差别不是很大

  1.2 线程的基本函数

基本函数介绍和使用:

  1. 线程创建:pthread_create

    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
    
    • thread:指向线程标识符的指针。
    • attr:线程属性,通常设为NULL表示使用默认属性。
    • start_routine:线程运行函数的起始地址。
    • arg:运行函数的参数。
  2. 线程终止:pthread_exit

    void pthread_exit(void *retval);
    
    • retval:线程的返回值。
  3. 等待线程终止:pthread_join

    int pthread_join(pthread_t thread, void **retval);
    
    • thread:等待终止的线程标识符。
    • retval:存储线程返回值的指针。
  4. 线程取消:pthread_cancel

    int pthread_cancel(pthread_t thread);
    
    • thread:要取消的线程标识符。
  5. 线程互斥锁:pthread_mutex_lock/unlock

    int pthread_mutex_lock(pthread_mutex_t *mutex);
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
    • mutex:互斥锁变量,用于同步线程对共享资源的访问。

 1.3 线程的基本使用例子:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>// 线程运行函数
void *print_message_function(void *ptr) {char *message;message = (char *) ptr;printf("%s \n", message);return NULL;
}int main() {pthread_t thread1, thread2;char *message1 = "Thread 1";char *message2 = "Thread 2";// 创建线程1if(pthread_create(&thread1, NULL, print_message_function, (void*) message1)) {printf("Error creating thread 1\n");return 1;}// 创建线程2if(pthread_create(&thread2, NULL, print_message_function, (void*) message2)) {printf("Error creating thread 2\n");return 1;}// 等待线程终止if(pthread_join(thread1, NULL)) {printf("Error joining thread 1\n");return 2;}if(pthread_join(thread2, NULL)) {printf("Error joining thread 2\n");return 2;}return 0;
}

在这个例子中:

  • 定义了一个print_message_function函数,它接收一个指向字符数组的指针,并打印出来。
  • main函数中,创建了两个线程thread1thread2,它们都执行print_message_function函数,但分别传递了不同的消息。
  • 使用pthread_create创建线程,使用pthread_join等待线程结束。

程序执行时,会创建两个线程,它们可能会并发地运行,打印出各自的消息。使用pthread_join确保了主程序会等待这两个线程完成它们的任务后再退出。

 二.线程的属性

线程属性(Thread Attributes)是可以用来定制线程的各种特性的集合。在创建线程时,可以指定线程的属性,如果不指定,线程会使用默认属性。线程属性通过pthread_attr_t结构体来表示,并且在使用前需要初始化。

常见的线程属性包括:

  • detachstate:线程的分离状态属性,可以设置为PTHREAD_CREATE_JOINABLE(默认值,可以调用pthread_join来等待线程结束)或PTHREAD_CREATE_DETACHED(线程一旦结束,其资源立即被回收,不能被等待)。
  • schedpolicy:线程的调度策略,可以是SCHED_OTHER(默认值,普通轮转调度),SCHED_FIFO(先来先服务调度),或SCHED_RR(时间片轮转调度)。
  • inheritsched:线程的继承调度策略,可以设置为PTHREAD_EXPLICIT_SCHED(显式指定线程的调度属性)或PTHREAD_INHERIT_SCHED(继承创建它的线程的调度属性,默认值)。
  • scope:线程的作用域,可以设置为PTHREAD_SCOPE_SYSTEM(与系统范围内的线程竞争CPU时间)或PTHREAD_SCOPE_PROCESS(仅与同一进程内的线程竞争CPU时间,通常是默认值)。

2.1线程属性使用例子

#include <pthread.h>int main() {pthread_attr_t attr;pthread_attr_init(&attr); // 初始化线程属性// 设置线程为分离状态pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 使用线程属性创建线程pthread_t thread;pthread_create(&thread, &attr, thread_function, NULL);// 清理线程属性pthread_attr_destroy(&attr);// ... 其他代码 ...return 0;
}

三.线程互斥

 3.1互斥锁

互斥锁(Mutex):

互斥锁是一种同步机制,用于防止多个线程同时访问共享资源。在多线程环境中,当多个线程尝试同时访问同一资源时,互斥锁确保任何时刻只有一个线程能够访问该资源。

3.2互斥锁常用函数 

常用的互斥锁函数:

  • 初始化互斥锁:pthread_mutex_init

    c

    复制

    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
    
    • mutex:指向互斥锁变量的指针。
    • attr:互斥锁属性,通常设为NULL表示使用默认属性。
  • 锁定互斥锁:pthread_mutex_lock

    c

    复制

    int pthread_mutex_lock(pthread_mutex_t *mutex);
    
    • mutex:指向互斥锁变量的指针。
  • 尝试锁定互斥锁:pthread_mutex_trylock

    c

    复制

    int pthread_mutex_trylock(pthread_mutex_t *mutex);
    
    • mutex:指向互斥锁变量的指针。如果互斥锁已经被锁定,函数不会阻塞,而是返回错误码。
  • 解锁互斥锁:pthread_mutex_unlock

    c

    复制

    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
    • mutex:指向互斥锁变量的指针。
  • 销毁互斥锁:pthread_mutex_destroy

    c

    复制

    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    
    • mutex:指向互斥锁变量的指针。

 3.3互斥锁使用例子

#include <pthread.h>// 全局变量
pthread_mutex_t lock; // 互斥锁
int shared_data = 0; // 共享数据// 线程函数
void *thread_function(void *arg) {pthread_mutex_lock(&lock); // 加锁shared_data++; // 访问共享数据pthread_mutex_unlock(&lock); // 解锁return NULL;
}int main() {pthread_mutex_init(&lock, NULL); // 初始化互斥锁pthread_t threads[10];for(int i = 0; i < 10; i++) {pthread_create(&threads[i], NULL, thread_function, NULL); // 创建线程}for(int i = 0; i < 10; i++) {pthread_join(threads[i], NULL); // 等待线程结束}pthread_mutex_destroy(&lock); // 销毁互斥锁printf("Shared data: %d\n", shared_data); // 输出共享数据的最终值return 0;
}

在这个例子中,我们创建了一个互斥锁lock来保护对shared_data的访问。每个线程在访问shared_data之前都会尝试锁定互斥锁,完成访问后解锁。这样可以确保在任一时刻只有一个。

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

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

相关文章

CLI举例:上下行连接路由器(路由引流)

CLI举例&#xff1a;上下行连接路由器&#xff08;路由引流&#xff09; 介绍了集群设备&#xff0c;上下行连接路由器的配置举例。 组网需求 如图1所示&#xff0c;上行网络使用BGP&#xff0c;下行网络使用OSPF&#xff0c;多数据中心统一通过路由器R4接入Internet。 希望…

solidworks卸载不干净怎么办?

当Solidworks卸载不干净时&#xff0c;您可以尝试以下方法来彻底清理卸载残留&#xff1a; 手动删除残留文件和注册表项&#xff1a; • 卸载程序&#xff1a;首先通过控制面板的“卸载或更改程序”功能尝试卸载Solidworks。 • 删除残留文件&#xff1a;在硬盘上找到Solidw…

数据结构之堆练习题及PriorityQueue深入讲解!

题外话 上午学了一些JavaEE初阶知识,下午继续复习数据结构内容 正题 本篇内容把堆的练习题做一下 第一题 1.下列关键字序列为堆的是:( A ) A: 100,60,70,50,32,65 B: 60,70,65,50,32,100 C: 65,100,70,32,50,60 D: 70,65,100,32,50,60 E: 32,50,100,70,65,60 …

HarmonyOS 开发-多模态页面转场动效实现案例

介绍 本示例介绍多模态页面转场动效实现&#xff1a;通过半模态转场实现半模态登录界面&#xff0c;通过配置NavDestinationMode类型为DIALOG&#xff0c;实现半模态的背景为透明&#xff0c;再与全屏模态和组件转场结合实现多模态组合登录场景&#xff0c;其中手机验证码登录…

第四次面试总结 — 嘉和智能 - 全栈开发

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信您对专栏 “本人真实面经” 很感兴趣o (ˉ▽ˉ&#xff1b;) 专栏 —— 本人真实面经&#xff0c;更多真实面试经验&#xff0c;中大厂面试总结等您挖掘 目录 总结&#xff08;非详细&#xff09; 面试内…

【leetcode面试经典150题】39. 赎金信(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

c# InvokeRequested delegate解决线程访问界面

说明 界面操作的代码应该写在界面&#xff08;Form/Dialog&#xff09;里 方法定义 private void Method1(string s) {...... }delegate定义 private delegate void Method1Delegate(string s);界面调用代码 private void op(){string sText"hello";if (this.In…

集合框架(数组,Arrays.sort,list,map,set,stack,queue)蓝桥杯习题

前言(基本知识) List集合 有序&#xff0c;接口&#xff0c; List<引用数据类型> listnew ArrayList<>(); 方法&#xff1a; add() size() get()//索引index从0开始&#xff0c;返回对应的值 isEmpty()判断是否包含该元素,不包含返回true&#xff0c;包含返…

从零开始为香橙派orangepi zero 3移植主线linux——2.kernel + rootfs

从零开始为香橙派orangepi zero 3移植主线linux——2.kernel rootfs 参考文章&#xff1a;一、linux kernel移植二、根文件系统2.1 buildroot构建1.修改toolchain下的交叉编译链2.修改系统配置3.去除内置kernel和uboot编译4.添加rootfs.tar格式的输出 2.2 ubuntu-base移植 三、…

代码随想录算法训练营第二十九天| 491.递增子序列、46.全排列、47全排列II

系列文章目录 目录 系列文章目录491.递增子序列回溯法使用 HashSet 作为哈希表进行树层去重使用 数组 作为哈希表进行树层去重&#xff08;最快&#xff09;使用 HashMap 作为哈希表进行树层去重 46.全排列回溯法使用used数组&#xff0c;标记已经选择的元素直接通过LinkedList…

python学习面向对象之继承的那些事儿

都说python里都是对像&#xff0c;所以选择了python&#xff0c;都说人生苦短&#xff0c;我用python&#xff0c;想用它当然就得了解它&#xff0c;爱它&#xff0c;并懂它。 序章 话说盘古开天辟地&#xff0c;女娲补天的时候&#xff0c;天地间一片混沌&#xff0c;女娲开…

Linux gcc day4 权限

首先来看比如ls、pwd等究竟是什么呢&#xff1f; 那ls是如何在shell中执行起来的呢&#xff1f;shell命令又是什么呢&#xff1f; shell命令&#xff1a;命令行解释器&#xff1a;表现&#xff1a;你看到的命令行提示符&#xff0c;以及可以输入指令并且可以执行&#xff08;是…

解决大模型,你是谁的问题

通过数据微调解决&#xff0c;数据如下 [{"id": "way0", "conversations": [{"from": "user", "value": "你是谁&#xff1f;"}, {"from": "assistant", "value": &quo…

贪心算法简介

目录 一、什么是贪心算法&#xff1f; 二、贪心算法的特点 三、贪心算法解决找零问题、最短路径问题、背包问题 1.找零问题 2.最短路径问题 3.背包问题 一、什么是贪心算法&#xff1f; 贪心算法就是希望通过局部最优来解决全局最优 基本步骤&#xff1a;1.将问题分为若…

【I/O】Unix IO 介绍

IO 模型&#xff08;一&#xff09; Unix IO 一个输入操作共包含两个阶段&#xff1a; 等待数据准备好从内核将数据复制到进程 对于一个套接字上的输入操作&#xff0c;通常第一步是等待数据从网络中到达&#xff0c;当数据到达时&#xff0c;先将数据复制到内核缓冲区中&a…

【UE 委托】如何利用函数指针理解委托的基本原理

目录 0 引言1 函数指针模拟多播委托 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;UE虚幻引擎专栏&#x1f4a5; 标题&#xff1a;【UE 委托】如何利用函数指针理解委托的基本原理❣️ 寄语&#xff1a;书到用时方恨少&#xff0c;事非经过不知难…

力扣239滑动窗口最大值——java中双端队列的一些知识

这道题. - 力扣&#xff08;LeetCode&#xff09;涉及到了双端队列的知识点&#xff0c;这里记录一下。Deque是可以从头部或者尾部来进行操作的。 Deque 是 Java 中双端队列&#xff08;Double Ended Queue&#xff09;的接口&#xff0c;它继承自 Queue 接口。双端队列允许在…

js base64 img 转 file

打印canvas图像的dataurl const imgDataUrl canvas.toDataURL();console.log(imgDataUrl)上传的图像编码 1,把base64编码转为文件对象第一个参数dataUrl是一个base64的字符串。第二个参数是文件名可以随意命名funtion base64toFile(dataurl, filename file) {let arr datau…

VSCode配置settings.json

快捷键 ctrl, 打开设置&#xff0c;右上角运行箭头的右边的那个图标&#xff0c;鼠标放到上面可以看到 “打开设置(json)” 点击它就能打开settings.json配置文件 常用的配置选项 "files.autoSave": "onWindowChange",// 当窗口失去焦点时&#xff0c;会…

Git 安装和配置

下载 Git 网址: https://git-scm.com/download 安装 Git 双击安装包, 开始安装. 修改安装路径, 选择非中文无空格路径: 开始安装: 安装成功: 配置 Git 安装完成后, 在任意文件夹内, 右键, 可以显示两个 Git 选项, 就说明安装成功了.