首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Linux篇】程控制全揭秘:如何通过 POSIX 库管理线程的生命周期

【Linux篇】程控制全揭秘:如何通过 POSIX 库管理线程的生命周期

作者头像
熬夜学编程的小王
发布2025-05-02 21:34:19
发布2025-05-02 21:34:19
20400
代码可运行
举报
文章被收录于专栏:编程小王编程小王
运行总次数:0
代码可运行
线程的神秘面纱:如何利用线程优化程序性能与响应能力 线程控制是多线程编程中的核心部分,它涉及如何创建、管理、终止和同步线程。操作系统通过线程控制提供对线程生命周期的管理,以确保多个线程能够高效、安全地执行。使用POSIX线程库(pthread)是实现线程控制的常见方式,它提供了一系列函数来创建线程、获取线程信息、同步线程以及处理线程终止和取消等问题。线程控制不仅能帮助程序员优化资源使用,还能提升程序的响应速度和并发能力。本节将深入探讨线程控制的各个方面,包括线程创建、终止、取消、等待、分离等操作,帮助读者理解如何在实际编程中灵活控制线程的行为,提升程序的并发性和稳定性。

💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力! 👍点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力! 🚀分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对Linux OS感兴趣的朋友,让我们一起进步!

一 线程

站在不同的用户角度线程的说法也不同,在用户角度,就称为线程;在操作系统角度称为轻量级进程(也叫做lwp)。

  • 查看轻量级进程指令如下:

ps -aL | head -1 && ps -aL | grep mythread

上述LWP就是轻量级进程,LWP也有自己的tid。

1.1 基本概念

线程(Thread)是操作系统进行任务调度和执行的基本单位,它是比进程(Process)更小的独立运行的单位。一个进程可以包含多个线程,这些线程共享进程的资源,例如内存和文件描述符。线程是CPU的调度基本单位,能够独立执行代码块。

1.2 线程控制

1.2.1 POSIX线程库
  • 引入背景

操作系统中只认识轻量级进程,而用户只认线程,操作中并没有线程概念,为了两者统一,使用中间层(POSIX库)来解决问题,该库通过封装与LWP相关的系统调用,来创建线程。

1.2.2 创建线程
  • 函数原型:

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

  • 功能: 创建一个新的线程。
  • 参数:
  1. pthread_t *thread:输出参数,用于存储新创建线程的唯一标识符(ID)。
  2. const pthread_attr_t *attr:指定线程属性(如栈大小、分离状态、调度策略等)。若设为 NULL,使用默认属性。
  3. void (start_routine)(void):新线程执行的入口函数。要求:函数签名必须为 void func(void*)。 返回值通常为 NULL(成功)或错误码(需转换为 void*)。
  4. void *arg:传递给线程函数的参数。
  • 返回值: 成功:返回 0。失败:返回错误码(需通过 strerror(err) 转换为可读信息)。
  • 示例代码:
代码语言:javascript
代码运行次数:0
运行
复制
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

void* print_message(void* arg) {
    pthread_t id =pthread_self();
    printf("子线程id: %ld\n",id);
    char* msg = (char*)arg;
    printf("Thread says: %s\n", msg);
    return NULL;
}

int main() {
    pthread_t tid;
    char* message = "Hello from thread!";
    
    // 创建线程(使用默认属性)
    if (pthread_create(&tid, NULL, print_message, message) != 0) {
        perror("Failed to create thread");
        return 1;
    }

    pthread_t id =pthread_self();//获取线程id
    printf("主线程id: %ld\n",id);

    // 主线程继续执行
    printf("Main thread continues...\n");
    
    // 等待子线程结束(避免主线程提前退出)
    pthread_join(tid, NULL);
    return 0;
}
}
  • 输出结果:

主线程id: 140160581412672 Main thread continues… 子线程id: 140160581408512 Thread says: Hello from thread!

从输出结果中可以看到两个线程的id值不同,同时非常大,这个id值是pthread库维持的,本质它是进程地址空间上的一个地址。这个 “ID” 作⽤域是进程级⽽⾮系统级(内核不认识)。

1.2.3 线程终止
  • 函数原型:

void pthread_exit(void *value_ptr);

  • 功能:

用于使调用线程终止执行,并且可以选择性地返回一个值给其他线程。

  • 参数:
  1. 参数 retval:指向线程返回值的指针。该值可通过 pthread_join 函数被其他线程获取。若无需返回值,可设为 NULL。注意:retval 必须指向全局变量或堆内存(如 malloc 分配),不可指向线程栈内存。线程退出后栈内存可能被回收,导致其他线程访问悬空指针。栈内存只有自己能看到,其它函数栈帧不可访问。
  • 返回值:

函数本身无返回值,调用后线程立即终止,后续代码不再执行。

  • 实力代码:
代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>

// 线程函数:计算字符串长度并返回
void* str_len_thread(void* arg) {
    const char* str = (const char*)arg;
    size_t len = strlen(str);
    // 分配堆内存存储结果
    size_t* result = (size_t*)malloc(sizeof(size_t));
    *result = len;
    pthread_exit(result); // 返回堆内存指针
}

int main() {
    pthread_t tid;
    const char* text = "Hello, pthread_exit!";
    
    // 创建线程
    if (pthread_create(&tid, NULL, str_len_thread, (void*)text) != 0) {
        perror("pthread_create failed");
        return 1;
    }

    // 等待线程结束并获取返回值
    void* retval;
    if (pthread_join(tid, &retval) != 0) {
        perror("pthread_join failed");
        return 1;
    }

    // 输出结果并释放堆内存
    printf("String length: %zu\n", *(size_t*)retval);
    free(retval);//释放堆内存
    return 0;
}

该程序是创建一个新线程并使用该线程来计算main栈帧里面的字符串的长度,并返回给主线程,这个返回值通过pthread_join函数获取。

1.2.4 线程取消
  • 函数原型:

int pthread_cancel(pthread_t thread);

  • 功能:

用于请求取消目标线程的函数。它向指定线程发送取消请求,但目标线程是否响应请求取决于其自身的取消状态和类型。

  • 参数: thread:要取消的目标线程的线程标识符(通过 pthread_create 返回)。
  • 返回值: 成功:返回 0。失败:返回错误码(如 ESRCH 表示线程不存在,EINVAL 表示无效参数)。
  • 示例代码:
代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* thread_func(void* arg) {
    printf("Thread started. Working...\n");
    // 模拟长时间运行的任务(非取消点)
    while (1) {
        sleep(1);
        printf("Working...\n");
    }
    return NULL;
}

int main() {
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);

    sleep(2); // 等待线程运行
    pthread_cancel(tid); // 发送取消请求

    void* retval;
    pthread_join(tid, &retval); // 等待线程终止

    if (retval == PTHREAD_CANCELED) {//线程被取消是,会返回PTHREAD_CANCELED宏值
        printf("Thread was canceled.\n");
    } else {
        printf("Thread exited normally.\n");
    }

    return 0;
}

输出结果:

Thread started. Working… Working… Thread was canceled.

该实例展示了,主线程创建新线程,主线程睡眠2秒,新线程每隔1秒进行工作,主线程2秒后取消新线程。

1.2.5 线程等待
  • 等待原因:

线程结束时,相应的资源仍在地址空间中,新线程不会使用未释放资源的空间,造成资源浪费。

  • 函数原型:

int pthread_join(pthread_t thread, void **value_ptr);

  • 功能: 用于等待目标线程终止并获取其返回值。资源回收:若目标线程未分离(未调用 pthread_detach),pthread_join 会回收其资源(如栈内存、线程描述符),避免僵尸线程。调⽤该函数的线程将挂起等待,直到id为thread的线程终⽌。
  • 参数:
  1. thread:要等待的线程标识符(通过 pthread_create 返回)。
  2. value_ptr:输出参数,用于接收目标线程的返回值(通过 pthread_exit 或线程函数返回)。若无需返回值,可设为 NULL。
  • 返回值: 成功:返回 0。失败:返回错误码(如 ESRCH 表示线程不存在,EDEADLK 表示死锁,EINVAL 表示无效参数)。
  • 示例代码:
代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

// 线程函数:计算1到N的和并返回
void* sum_thread(void* arg) {
    int n = *(int*)arg;
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        sum += i;
    }
    // 返回堆内存指针(需外部释放)
    int* result = (int*)malloc(sizeof(int));
    *result = sum;
    pthread_exit(result);
}

int main() {
    pthread_t tid;
    int n = 10;

    // 创建线程
    if (pthread_create(&tid, NULL, sum_thread, &n) != 0) {
        perror("pthread_create failed");
        return 1;
    }

    // 等待线程结束并获取返回值
    void* retval;
    if (pthread_join(tid, &retval) != 0) {
        perror("pthread_join failed");
        return 1;
    }

    // 输出结果并释放堆内存
    int sum = *(int*)retval;
    printf("Sum of 1 to %d: %d\n", n, sum);
    free(retval);
    return 0;
}

该示例主线程创建新线程完成1至N的累加,新线程并通过pthread_exit()函数将结果返回,主线程通过pthread_join()函数获取新线程的返回状态。

1.2.6 线程分离
  • 函数原型:

int pthread_detach(pthread_t thread);

  • 功能:

用于分离目标线程的核心函数。分离后的线程在终止时会自动回收资源,无需通过 pthread_join 手动回收。

  • 参数 :

thread:要分离的线程标识符(通过 pthread_create 返回)。

  • 返回值:

成功:返回 0。失败:返回错误码(如 ESRCH 表示线程不存在,EINVAL 表示无效参数)。

  • 示例代码:
代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 线程函数:后台任务(无需返回值)
void* background_task(void* arg) {
    printf("Background thread started. Working...\n");
    sleep(2); // 模拟耗时操作
    printf("Background thread finished.\n");
    return NULL; // 返回值无法被获取
}

int main() {
    pthread_t tid;
    // 创建线程并立即分离
    if (pthread_create(&tid, NULL, background_task, NULL) != 0) {
        perror("pthread_create failed");
        return 1;
    }

    if (pthread_detach(tid) != 0) {
        perror("pthread_detach failed");
        return 1;
    }

    // 主线程继续执行其他任务
    printf("Main thread continues...\n");
    sleep(3); // 确保子线程有机会运行
    return 0;
}

该实例主线程创建新线程后,分离线程,睡眠3秒保证子线程可以正常运行自己的代码。注意:主线程运行完毕,子线程并不会立即退出,分离的子线程会继续运行,直到进程终止或自身结束。

1.3 线程优点与缺点

  • 优点
  • 提高资源利用率:线程可以共享进程的内存空间和文件描述符等资源,避免了进程间通信(IPC)的开销。通过多线程,可以更有效地利用CPU资源,特别是在等待I/O操作时,其他线程可以继续执行,从而提高整体吞吐量。
  • 提高响应速度: 多线程程序可以同时处理多个任务,使得用户界面更加流畅,响应速度更快。例如,在图形用户界面(GUI)应用程序中,一个线程可以处理用户输入,而另一个线程可以执行后台任务,如文件操作或网络通信。
  • 简化编程模型: 线程提供了更自然的编程模型,使得并发编程更加简单。开发者可以更容易地编写和调试并发程序,而无需处理复杂的进程间通信和同步机制。
  • 支持并发执行: 线程是轻量级的,创建和销毁线程的开销相对较小。这使得线程成为实现高并发应用程序的理想选择,如Web服务器、数据库服务器等。
  • 缺点
  • 增加编程复杂度: 虽然线程简化了并发编程模型,但多线程程序仍然比单线程程序更复杂。开发者需要处理线程同步、死锁、竞态条件等问题,这增加了编程的难度和出错的可能性。
  • 资源竞争和同步开销: 当多个线程同时访问共享资源时,可能会发生资源竞争,导致数据不一致或程序崩溃。为了避免这些问题,需要使用同步机制(如互斥锁、信号量等),但这些机制本身也会带来一定的开销。
  • 调试和测试困难: 多线程程序的调试和测试比单线程程序更加困难。由于线程的执行顺序不确定,很难重现和调试特定的错误场景。
  • 对操作系统和硬件的依赖: 线程的实现和调度依赖于操作系统和硬件的支持。不同操作系统和硬件平台上的线程行为可能有所不同,这增加了跨平台开发的复杂性。

1.4 线程异常

  • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃
  • 线程是进程的执⾏分⽀,线程出异常,就类似进程出异常,进⽽触发信号机制,终⽌进程,进程 终⽌,该进程内的所有线程也就随即退出

1.5 线程用途

1.5.1 线程的基本用途
  • 并发执行:

线程允许程序同时执行多个任务,从而充分利用多核处理器的计算能力,提高CPU的利用率和程序的执行效率。例如,在Web服务器中,可以同时处理多个客户端请求,每个请求由一个独立的线程处理,从而显著提高服务器的并发能力和响应速度。

  • 提高响应速度:

在需要快速响应的用户界面(如GUI应用程序)中,线程可以确保用户界面保持流畅,即使后台有耗时的任务在执行。例如,一个线程可以专门处理用户输入,而另一个线程则执行文件操作或网络通信等后台任务,从而避免用户界面卡顿。

  • 资源共享:

线程共享进程的内存空间和文件描述符等资源,这减少了进程间通信(IPC)的开销,并促进了数据在任务间的共享。例如,在多线程应用程序中,多个线程可以访问和修改同一个全局变量或共享数据结构,而无需进行复杂的进程间通信。

1.5.2 线程的高级用途
  • 异步处理:

通过线程实现异步操作,可以在不阻塞主线程的情况下执行耗时任务,从而提高程序的响应性和用户体验。例如,在图形用户界面(GUI)应用程序中,可以使用一个单独的线程来执行文件下载或网络请求等耗时操作,而主线程则继续处理用户输入和界面更新。

  • 分布式计算:

将任务分配给多个线程,利用多核处理器的优势进行并行计算,可以显著加速大规模数据处理和复杂计算任务。例如,在科学计算、图像处理和机器学习等领域,多线程并行计算已成为提高计算速度和效率的重要手段。

  • 实时系统:

在需要实时响应的系统中,线程可以确保关键任务及时执行,满足系统的实时性要求。例如,在嵌入式系统、实时操作系统(RTOS)和工业自动化控制等领域,线程被广泛应用于实现实时任务调度和响应。

1.5.3 线程在特定领域的应用
  • Web服务器:

Web服务器使用多线程来处理多个客户端请求,每个请求由一个独立的线程处理。这提高了服务器的并发能力和响应速度,使得服务器能够同时处理大量请求。

  • 图形处理:

在图形处理领域,多线程并行计算被广泛应用于加速图像渲染、视频编码和解码等任务。通过将图像数据分割成多个块,并分配给不同的线程进行处理,可以显著提高图形处理的速度和效率。

  • 科学计算:

科学计算领域经常需要处理大规模的数据集和复杂的计算任务。多线程并行计算可以显著加速这些任务的执行速度,提高计算效率。例如,在气候模拟、基因测序和物理模拟等领域,多线程并行计算已成为不可或缺的工具。

1.6 线程 VS 进程

特性

进程(Process)

线程(Thread)

资源

独立的内存空间、文件描述符等资源

共享进程的内存空间、资源

开销

创建和销毁进程的开销较大

创建和销毁线程的开销较小

通信方式

进程间通信较为复杂(IPC)

线程间通信简单,可以直接访问共享数据

调度

进程切换较慢,需要保存和恢复大量上下文信息

线程切换较快,切换上下文的开销较小

并发性

进程可以并行执行,适合独立任务

线程并发执行,适合共享资源的任务

稳定性

进程之间相互独立,一个进程崩溃不会影响其他进程

线程共享资源,若一个线程崩溃,可能影响整个进程

总的来说,进程是操作系统的基本执行单位,而线程是进程内的执行单元。线程通常用于实现多任务并发和提高程序的效率,而进程则适用于需要较强隔离性和独立性的任务。

二. 最后

本文系统阐述线程技术:线程是CPU调度的基本单位,共享进程资源,通过POSIX库实现创建、终止、取消、等待和分离等操作。其优势在于提升资源利用率、响应速度和并发能力,但需应对同步复杂度和死锁风险。线程适用于异步处理、分布式计算及Web服务等领域,与进程相比具有更轻量、通信更高效的特点,但进程崩溃独立性更强。文章还分析了线程异常对进程的影响及多线程编程的注意事项,为开发者提供了全面的技术参考。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 线程的神秘面纱:如何利用线程优化程序性能与响应能力 线程控制是多线程编程中的核心部分,它涉及如何创建、管理、终止和同步线程。操作系统通过线程控制提供对线程生命周期的管理,以确保多个线程能够高效、安全地执行。使用POSIX线程库(pthread)是实现线程控制的常见方式,它提供了一系列函数来创建线程、获取线程信息、同步线程以及处理线程终止和取消等问题。线程控制不仅能帮助程序员优化资源使用,还能提升程序的响应速度和并发能力。本节将深入探讨线程控制的各个方面,包括线程创建、终止、取消、等待、分离等操作,帮助读者理解如何在实际编程中灵活控制线程的行为,提升程序的并发性和稳定性。
  • 一 线程
    • 1.1 基本概念
    • 1.2 线程控制
      • 1.2.1 POSIX线程库
      • 1.2.2 创建线程
      • 1.2.3 线程终止
      • 1.2.4 线程取消
      • 1.2.5 线程等待
      • 1.2.6 线程分离
    • 1.3 线程优点与缺点
    • 1.4 线程异常
    • 1.5 线程用途
      • 1.5.1 线程的基本用途
      • 1.5.2 线程的高级用途
      • 1.5.3 线程在特定领域的应用
    • 1.6 线程 VS 进程
  • 二. 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档