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

Linux学习笔记:

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

前言:

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

目录

一、线程的基本概念

[1.1 什么是线程?](#1.1 什么是线程?)

[1.2 线程与进程的区别](#1.2 线程与进程的区别)

[1.3 线程的优点](#1.3 线程的优点)

[1.4 线程的缺点](#1.4 线程的缺点)

二、Linux中的线程模型

[2.1 用户级线程与内核级线程](#2.1 用户级线程与内核级线程)

[2.2 Linux的线程实现](#2.2 Linux的线程实现)

三、线程控制

[3.1 线程的创建与终止](#3.1 线程的创建与终止)

[3.1.1 创建线程](#3.1.1 创建线程)

[3.1.2 终止线程](#3.1.2 终止线程)

[3.2 线程的属性](#3.2 线程的属性)

[3.3 线程的取消](#3.3 线程的取消)

[3.4 线程的清理](#3.4 线程的清理)

四、线程分离

[4.1 什么是线程分离?](#4.1 什么是线程分离?)

[4.2 设置线程为分离状态](#4.2 设置线程为分离状态)

[4.3 线程分离的注意事项](#4.3 线程分离的注意事项)

五、线程的调度

[5.1 线程的调度策略](#5.1 线程的调度策略)

[5.2 设置线程的调度策略](#5.2 设置线程的调度策略)

五、总结


一、线程的基本概念

1.1 什么是线程?

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

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

1.2 线程与进程的区别

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

1.3 线程的优点

  1. 响应性:多线程程序可以在一个线程等待I/O时,另一个线程继续执行,从而提高程序的响应性。

  2. 资源共享:线程共享进程的内存空间,因此线程间的数据共享和通信更加方便。

  3. 经济性:创建和销毁线程的开销比进程小,且线程切换的开销也比进程小。

  4. 多核利用:多线程程序可以充分利用多核CPU的并行计算能力。

1.4 线程的缺点

  1. 同步问题:多个线程共享同一进程的资源,容易引发竞态条件等问题。

  2. 调试困难:多线程程序的调试比单线程程序复杂,因为线程的执行顺序是不确定的。

  3. 资源竞争:多个线程竞争同一资源时,可能导致性能下降。

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

二、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函数来创建线程。该函数的原型如下:

cpp 复制代码
#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:传递给线程函数的参数。

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

cpp 复制代码
#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 终止线程

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

  1. 正常返回:线程函数执行完毕并返回,线程自动终止。

  2. 调用pthread_exit :线程可以调用pthread_exit函数主动终止自己。

  3. 被其他线程取消 :其他线程可以调用pthread_cancel函数取消指定线程。

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

cpp 复制代码
#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、轮转等)。

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

cpp 复制代码
#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等。

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

cpp 复制代码
#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函数来注册和注销清理函数。

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

cpp 复制代码
#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函数将线程设置为分离状态。下面是一个线程分离的示例:

cpp 复制代码
#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 线程分离的注意事项

  1. 资源释放 :分离线程在终止时会自动释放资源,因此不需要调用pthread_join来回收资源。

  2. 无法获取返回值:分离线程的返回值无法被其他线程获取,因为线程终止后资源已经被释放。

  3. 线程状态 :一旦线程被设置为分离状态,就不能再通过pthread_join来等待线程结束。

五、线程的调度

5.1 线程的调度策略

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

  • SCHED_FIFO:先进先出调度策略,优先级高的线程会一直运行,直到它主动放弃CPU。

  • SCHED_RR:轮转调度策略,优先级高的线程会运行一段时间(时间片),然后让出CPU给其他相同优先级的线程。

  • SCHED_OTHER:默认的调度策略,基于时间片的动态优先级调度。

5.2 设置线程的调度策略

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

cpp 复制代码
#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中线程的基本概念和线程控制的相关知识,包括线程的创建与终止、线程属性、线程的取消与清理、线程的调度等内容。通过代码示例,读者可以更好地理解这些概念,并在实际编程中应用这些知识。

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

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

相关推荐
ChinaRainbowSea42 分钟前
1. Linux下 MySQL 的详细安装与使用
linux·数据库·sql·mysql·adb
wen__xvn43 分钟前
每日一题洛谷P1914 小书童——凯撒密码c++
数据结构·c++·算法
网络安全(华哥)1 小时前
网络安全服务实施流程管理 网络安全服务体系
运维·服务器·网络
致奋斗的我们1 小时前
Nginx反向代理及负载均衡
linux·运维·mysql·nginx·负载均衡·shell·openeluer
百锦再1 小时前
在Linux上创建一个Docker容器并在其中执行Python脚本
linux·python·docker
Ares-Wang1 小时前
负载均衡 方式
运维·负载均衡
钗头风2 小时前
3.Docker常用命令
运维·docker·容器
BUG 劝退师2 小时前
八大经典排序算法
数据结构·算法·排序算法
陈无左耳、2 小时前
HarmonyOS学习第2天: 解锁语言与框架的无限可能
学习·华为·harmonyos
忧虑的乌龟蛋2 小时前
嵌入式 Linux:使用设备树驱动GPIO全流程
linux·服务器·嵌入式·imx6ull·gpio·点灯·pinctrl