【Linux探索学习】第二十九弹——线程概念:Linux线程的基本概念与线程控制详解

【Linux探索学习】第二十九弹——线程概念:Linux线程的基本概念与线程控制详解

Linux学习笔记:

https://blog.ZEEKLOG.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482

前言:

在现代操作系统中,线程是程序执行流的最小单元。与进程相比,线程更加轻量级,创建和销毁的开销更小,且线程之间可以共享内存空间,因此在多任务处理、并发编程中,线程的使用非常广泛。Linux作为一个多用户、多任务的操作系统,提供了强大的线程支持。本文将详细介绍Linux中线程的基本概念以及线程控制的相关知识,并通过代码示例帮助读者更好地理解。

目录

一、线程的基本概念

1.1 什么是线程?

1.2 线程与进程的区别

1.3 线程的优点

1.4 线程的缺点

二、Linux中的线程模型

2.1 用户级线程与内核级线程

2.2 Linux的线程实现

三、线程控制

3.1 线程的创建与终止

3.1.1 创建线程

3.1.2 终止线程

3.2 线程的属性

3.3 线程的取消

3.4 线程的清理

四、线程分离

4.1 什么是线程分离?

4.2 设置线程为分离状态

4.3 线程分离的注意事项

五、线程的调度

5.1 线程的调度策略

5.2 设置线程的调度策略

五、总结


一、线程的基本概念

1.1 什么是线程?

线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程共享进程的资源(如内存、文件描述符等),但每个线程拥有独立的执行栈和程序计数器。

线程其实就是我们之前所讲的进程的延申,进程实际上是可以由多个执行流组成的,这些执行流其实就叫做线程,我们之前所讲的进程实际上就是只有单执行流的特殊情况,进程实际上可以看作资源分配的实体,线程就是资源分配的基本单位

1.2 线程与进程的区别

特性进程线程
定义程序的一次执行,拥有独立的内存空间进程中的一个执行流,共享进程的内存空间
资源开销较大,创建和销毁开销大较小,创建和销毁开销小
通信方式进程间通信(IPC)机制复杂线程间通信简单,直接共享内存
上下文切换开销大开销小
独立性独立,互不影响依赖进程,线程间相互影响

1.3 线程的优点

响应性:多线程程序可以在一个线程等待I/O时,另一个线程继续执行,从而提高程序的响应性。资源共享:线程共享进程的内存空间,因此线程间的数据共享和通信更加方便。经济性:创建和销毁线程的开销比进程小,且线程切换的开销也比进程小。多核利用:多线程程序可以充分利用多核CPU的并行计算能力。

1.4 线程的缺点

同步问题:多个线程共享同一进程的资源,容易引发竞态条件等问题。调试困难:多线程程序的调试比单线程程序复杂,因为线程的执行顺序是不确定的。资源竞争:多个线程竞争同一资源时,可能导致性能下降。

这些问题我们会在后面的章节进行解决,尤其是线程的同步与互斥问题是很重要的,我们后面会进行讲解

二、Linux中的线程模型

2.1 用户级线程与内核级线程

在Linux中,线程的实现可以分为用户级线程和内核级线程。

用户级线程:由用户空间的线程库(如POSIX线程库)管理,内核并不知道这些线程的存在。用户级线程的创建、调度、同步等操作都由线程库在用户空间完成。优点是线程切换开销小,缺点是无法利用多核CPU的并行能力。内核级线程:由操作系统内核管理,内核知道每个线程的存在,并负责线程的调度。内核级线程的创建、调度、同步等操作都需要通过系统调用来完成。优点是可以利用多核CPU的并行能力,缺点是线程切换开销较大。

2.2 Linux的线程实现

Linux通过轻量级进程(Lightweight Process, LWP)来实现线程。每个线程在内核中都有一个对应的轻量级进程,这些轻量级进程共享同一地址空间和其他资源。Linux的线程库(如NPTL)提供了对POSIX线程标准的支持。我们可以理解为Linux的线程实现就是用户层的,因为Linux中并没有线程的概念,有的只是轻量级进程的概念,是通过在用户层进行封装来实现的

三、线程控制

3.1 线程的创建与终止

在Linux中,线程的创建和终止是通过POSIX线程库(pthread)来实现的。下面我们通过代码示例来讲解线程的创建与终止。

3.1.1 创建线程

在POSIX线程库中,使用pthread_create函数来创建线程。该函数的原型如下:

#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
thread:指向线程标识符的指针。attr:用于设置线程属性,通常为NULL,表示使用默认属性。start_routine:线程函数的起始地址,线程创建后会执行该函数。arg:传递给线程函数的参数。

下面是一个简单的线程创建示例:

#include <stdio.h> #include <pthread.h> #include <unistd.h> void* thread_function(void* arg) { printf("Thread is running...\n"); sleep(2); printf("Thread is exiting...\n"); return NULL; } int main() { pthread_t thread_id; int ret = pthread_create(&thread_id, NULL, thread_function, NULL); if (ret != 0) { printf("Thread creation failed!\n"); return 1; } printf("Main thread is running...\n"); pthread_join(thread_id, NULL); // 等待线程结束 printf("Main thread is exiting...\n"); return 0; }

运行结果:

在这个示例中,主线程创建了一个新线程,新线程执行thread_function函数。主线程通过pthread_join函数等待新线程结束。

3.1.2 终止线程

线程可以通过以下方式终止:

正常返回:线程函数执行完毕并返回,线程自动终止。调用pthread_exit:线程可以调用pthread_exit函数主动终止自己。被其他线程取消:其他线程可以调用pthread_cancel函数取消指定线程。

下面是一个使用pthread_exit终止线程的示例:

#include <stdio.h> #include <pthread.h> #include <unistd.h> void* thread_function(void* arg) { printf("Thread is running...\n"); sleep(2); printf("Thread is exiting...\n"); pthread_exit(NULL); // 主动终止线程 } int main() { pthread_t thread_id; int ret = pthread_create(&thread_id, NULL, thread_function, NULL); if (ret != 0) { printf("Thread creation failed!\n"); return 1; } printf("Main thread is running...\n"); pthread_join(thread_id, NULL); // 等待线程结束 printf("Main thread is exiting...\n"); return 0; }

运行结果:

3.2 线程的属性

线程的属性可以通过pthread_attr_t结构体来设置。常见的线程属性包括:

线程的分离状态:线程可以是可连接的(joinable)或分离的(detached)。可连接的线程在终止后需要其他线程调用pthread_join来回收资源,而分离的线程在终止后会自动释放资源。线程的栈大小:可以设置线程的栈大小。线程的调度策略:可以设置线程的调度策略(如FIFO、轮转等)。

下面是一个设置线程属性的示例:

#include <stdio.h> #include <pthread.h> #include <unistd.h> void* thread_function(void* arg) { printf("Thread is running...\n"); sleep(2); printf("Thread is exiting...\n"); return NULL; } int main() { pthread_t thread_id; pthread_attr_t attr; pthread_attr_init(&attr); // 初始化线程属性 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 设置线程为分离状态 int ret = pthread_create(&thread_id, &attr, thread_function, NULL); if (ret != 0) { printf("Thread creation failed!\n"); return 1; } printf("Main thread is running...\n"); sleep(3); // 主线程等待一段时间,确保子线程执行完毕 printf("Main thread is exiting...\n"); pthread_attr_destroy(&attr); // 销毁线程属性 return 0; }

运行结果:

在这个示例中,我们通过pthread_attr_setdetachstate函数将线程设置为分离状态,这样线程在终止后会自动释放资源,主线程不需要调用pthread_join来等待线程结束。

3.3 线程的取消

线程可以通过pthread_cancel函数来取消。被取消的线程会在下一个取消点(cancellation point)终止。取消点通常是某些系统调用或库函数,如sleepreadwrite等。

下面是一个线程取消的示例:

#include <stdio.h> #include <pthread.h> #include <unistd.h> void* thread_function(void* arg) { printf("Thread is running...\n"); while (1) { printf("Thread is working...\n"); sleep(1); } return NULL; } int main() { pthread_t thread_id; int ret = pthread_create(&thread_id, NULL, thread_function, NULL); if (ret != 0) { printf("Thread creation failed!\n"); return 1; } sleep(3); // 主线程等待3秒 pthread_cancel(thread_id); // 取消线程 printf("Thread has been canceled.\n"); pthread_join(thread_id, NULL); // 等待线程结束 printf("Main thread is exiting...\n"); return 0; }

运行结果:

在这个示例中,主线程在3秒后取消了子线程,子线程在sleep函数处被取消。

3.4 线程的清理

线程在终止时可能需要执行一些清理操作,如释放资源、关闭文件等。POSIX线程库提供了pthread_cleanup_pushpthread_cleanup_pop函数来注册和注销清理函数。

下面是一个线程清理的示例:

#include <stdio.h> #include <pthread.h> #include <unistd.h> void cleanup_function(void* arg) { printf("Cleanup function is called: %s\n", (char*)arg); } void* thread_function(void* arg) { pthread_cleanup_push(cleanup_function, "Resource 1"); pthread_cleanup_push(cleanup_function, "Resource 2"); printf("Thread is running...\n"); sleep(2); printf("Thread is exiting...\n"); pthread_cleanup_pop(1); // 执行清理函数 pthread_cleanup_pop(1); // 执行清理函数 return NULL; } int main() { pthread_t thread_id; int ret = pthread_create(&thread_id, NULL, thread_function, NULL); if (ret != 0) { printf("Thread creation failed!\n"); return 1; } pthread_join(thread_id, NULL); // 等待线程结束 printf("Main thread is exiting...\n"); return 0; }

在这个示例中,我们使用pthread_cleanup_push注册了两个清理函数,当线程终止时,这些清理函数会被自动调用。

四、线程分离

4.1 什么是线程分离?

线程分离(Detached Thread)是指线程在终止后自动释放其资源,而不需要其他线程调用pthread_join来回收资源。分离线程通常用于不需要返回结果的场景。

4.2 设置线程为分离状态

可以通过pthread_detach函数将线程设置为分离状态。下面是一个线程分离的示例:

#include <stdio.h> #include <pthread.h> #include <unistd.h> void* thread_function(void* arg) { printf("Thread is running...\n"); sleep(2); printf("Thread is exiting...\n"); return NULL; } int main() { pthread_t thread_id; int ret = pthread_create(&thread_id, NULL, thread_function, NULL); if (ret != 0) { printf("Thread creation failed!\n"); return 1; } pthread_detach(thread_id); // 将线程设置为分离状态 printf("Main thread is running...\n"); sleep(3); // 主线程等待一段时间,确保子线程执行完毕 printf("Main thread is exiting...\n"); return 0; }

在这个示例中,我们通过pthread_detach函数将线程设置为分离状态。分离状态的线程在终止后会自动释放资源,主线程不需要调用pthread_join来等待线程结束。

4.3 线程分离的注意事项

资源释放:分离线程在终止时会自动释放资源,因此不需要调用pthread_join来回收资源。无法获取返回值:分离线程的返回值无法被其他线程获取,因为线程终止后资源已经被释放。线程状态:一旦线程被设置为分离状态,就不能再通过pthread_join来等待线程结束。

五、线程的调度

5.1 线程的调度策略

Linux中的线程调度策略主要有以下几种:

SCHED_FIFO:先进先出调度策略,优先级高的线程会一直运行,直到它主动放弃CPU。SCHED_RR:轮转调度策略,优先级高的线程会运行一段时间(时间片),然后让出CPU给其他相同优先级的线程。SCHED_OTHER:默认的调度策略,基于时间片的动态优先级调度。

5.2 设置线程的调度策略

可以通过pthread_setschedparam函数来设置线程的调度策略和优先级。下面是一个设置线程调度策略的示例:

#include <stdio.h> #include <pthread.h> #include <sched.h> #include <unistdh> void* thread_function(void* arg) { printf("Thread is running...\n"); sleep(2); printf("Thread is exiting...\n"); return NULL; } int main() { pthread_t thread_id; pthread_attr_t attr; struct sched_param param; pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_FIFO); // 设置调度策略为FIFO param.sched_priority = 50; // 设置优先级 pthread_attr_setschedparam(&attr, &param); int ret = pthread_create(&thread_id, &attr, thread_function, NULL); if (ret != 0) { printf("Thread creation failed!\n"); return 1; } pthread_join(thread_id, NULL); // 等待线程结束 printf("Main thread is exiting...\n"); pthread_attr_destroy(&attr); // 销毁线程属性 return 0; }

运行结果:

在这个示例中,我们通过pthread_attr_setschedpolicy函数将线程的调度策略设置为SCHED_FIFO,并通过pthread_attr_setschedparam函数设置了线程的优先级。

五、总结

本文详细介绍了Linux中线程的基本概念和线程控制的相关知识,包括线程的创建与终止、线程属性、线程的取消与清理、线程的调度等内容。通过代码示例,读者可以更好地理解这些概念,并在实际编程中应用这些知识。

有关线程的一些基本概念我们可以通过下面的图片自行查看一下,比如线程与进程之间的地址空间分配问题,线程所拥有的独立的栈区是怎么一回事等

感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

Read more

(第二篇)Spring AI 实战进阶:从 0 搭建 SaaS 模式多租户 AI 客服平台(核心难点 + 性能优化全解析)

(第二篇)Spring AI 实战进阶:从 0 搭建 SaaS 模式多租户 AI 客服平台(核心难点 + 性能优化全解析)

前言 随着 AI 大模型技术的普及,智能客服已成为企业降本增效的核心工具,但传统的单租户 AI 客服系统无法满足 SaaS 平台的规模化需求 —— 不同租户需要独立的模型配置、数据隔离、流量管控,同时还要保证高并发下的性能稳定性。 笔者近期主导了基于 Spring AI 的多租户 AI 客服 SaaS 平台开发,踩遍了多租户模型隔离、缓存隔离、流量控制、高并发优化等核心坑点。本文将从实战角度,完整拆解 SaaS 模式 AI 客服平台的开发全流程:从架构设计到核心难点突破,从功能实现到性能压测优化,所有代码均为生产环境可直接复用的实战代码,同时结合可视化图表清晰呈现核心逻辑,希望能给做 AI SaaS 开发的同学提供有价值的参考。 一、项目背景与架构设计 1.1 项目定位与核心需求 项目定位:SaaS 模式的智能客服解决方案,支持多企业租户接入,每个租户可自定义

By Ne0inhk
国产五大AI模型哪家强?DeepSeek、豆包、Kimi、智谱清言、通义千问深度解析!哪款大模型更适合你?

国产五大AI模型哪家强?DeepSeek、豆包、Kimi、智谱清言、通义千问深度解析!哪款大模型更适合你?

今天我们来聊聊当下最火的五款国产AI大模型——DeepSeek、豆包、Kimi、智谱清言和通义千问。 它们各有千秋,有的擅长专业分析,有的专攻娱乐互动,还有的靠“长文本”出圈……究竟谁更适合我们的需求?看完这篇就懂了! 一、DeepSeek:高性价比推理强者 DeepSeek是深度求索推出的大语言模型,堪称2025年AI界的“黑马”。它推理能力超强,表现和GPT-4不相上下。春节期间的爆火,使其成为史上用户增速最快的AI应用。 1、特点 语义理解能力强: 能吃透问题,给出精准答案。比如搞学术研究,我们问专业领域的复杂问题,它可以快速翻找资料,整理出关键信息,条理清晰地解答,帮我们省下不少时间。写论文时,还能帮着生成大纲、分析文献综述。 开源与本地化支持: 完全开源,支持本地部署,用户可以根据需求定制模型,同时完全掌控数据隐私。 其API服务价格亲民,输入/输出每百万tokens的成本分别为2元和8元,适合中小企业和个人开发者使用。 高性能与低成本: 在多项基准测试中,DeepSeek的表现接近甚至超越了Claude-Sonnet和GPT-4o等国际顶尖模型。其训练成

By Ne0inhk
人工智能:自然语言处理在客户服务领域的应用与实战

人工智能:自然语言处理在客户服务领域的应用与实战

人工智能:自然语言处理在客户服务领域的应用与实战 学习目标 💡 理解自然语言处理(NLP)在客户服务领域的应用场景和重要性 💡 掌握客户服务领域NLP应用的核心技术(如聊天机器人、意图识别、情感分析) 💡 学会使用前沿模型(如BERT、GPT-3)进行客户服务文本分析 💡 理解客户服务领域的特殊挑战(如实时性要求、多语言处理、用户体验) 💡 通过实战项目,开发一个客户服务聊天机器人应用 重点内容 * 客户服务领域NLP应用的主要场景 * 核心技术(聊天机器人、意图识别、情感分析) * 前沿模型(BERT、GPT-3)在客户服务领域的使用 * 客户服务领域的特殊挑战 * 实战项目:客户服务聊天机器人应用开发 一、客户服务领域NLP应用的主要场景 1.1 聊天机器人 1.1.1 聊天机器人的基本概念 聊天机器人是通过自然语言与用户进行交互的程序。在客户服务领域,聊天机器人的主要应用场景包括: * 客户服务:回答客户的问题(如“如何退货”、“商品价格”

By Ne0inhk