Linux线程编程完全指南:从原理到实践

一、线程基础概念

1.1 什么是线程?

在Linux系统中,线程是轻量级的进程,它们属于某个进程,共享进程的资源,但拥有独立的执行流。

核心特征

  • 进程是系统中最小的资源分配单位

  • 线程是系统中最小的执行单位

  • 进程中,线程与线程是平级关系

  • 进程中默认有一个主线程(main函数所在线程)

1.2 线程与进程的核心区别

特性

进程

线程

资源共享

资源独立,不共享

共享进程资源(全局变量、堆、文件描述符等)

栈空间

独立地址空间

每个线程有独立栈区(默认8MB),但共享堆和全局区

稳定性

相对稳定,一个进程崩溃不影响其他进程

不稳定,一个线程崩溃会导致整个进程崩溃

创建开销

大(需创建3GB虚拟地址空间)

小(只需在进程空间中分配新的栈区)

并发度

较低,上下文切换开销大

较高,上下文切换开销小

通信方式

复杂(管道、消息队列、共享内存等)

简单(直接读写共享变量)

1.3 线程的主要作用

  1. 并发执行:充分利用多核CPU,提高程序性能

  2. 处理耗时任务:将耗时操作放到后台线程,保持界面响应

  3. 异步处理:处理I/O密集型任务,避免阻塞主线程

二、线程编程步骤(POSIX标准)

线程编程通常遵循以下步骤:

1. 创建线程 (pthread_create)
2. 线程执行任务 (线程函数)
3. 线程退出 (pthread_exit/return)
4. 线程资源回收 (pthread_join/pthread_detach)

三、线程相关函数详解

3.1 线程创建:pthread_create

#include <pthread.h>

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

参数解析

参数

说明

示例

thread

线程ID指针,函数返回创建的线程ID

pthread_t tid;

attr

线程属性,NULL表示默认属性

NULL

start_routine

线程函数指针,线程执行的入口

void* thread_func(void* arg)

arg

传递给线程函数的参数

任意类型指针

返回值

  • 成功:返回0

  • 失败:返回错误码(非0值)

完整示例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 线程函数
void* thread_func(void* arg) {
    int thread_num = *(int*)arg;
    printf("线程%d启动,线程ID: %lu\n", thread_num, pthread_self());
    sleep(2);
    printf("线程%d结束\n", thread_num);
    return NULL;
}

int main() {
    pthread_t tid1, tid2;
    int arg1 = 1, arg2 = 2;
    
    // 创建线程1
    if (pthread_create(&tid1, NULL, thread_func, &arg1) != 0) {
        perror("线程1创建失败");
        return 1;
    }
    
    // 创建线程2
    if (pthread_create(&tid2, NULL, thread_func, &arg2) != 0) {
        perror("线程2创建失败");
        return 1;
    }
    
    // 等待线程结束
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    
    printf("主线程结束\n");
    return 0;
}

3.2 获取线程ID:pthread_self

pthread_t pthread_self(void);

功能:获取当前线程的线程ID

参数:无

返回值:当前线程的ID

示例

printf("当前线程ID: %lu\n", pthread_self());

线程ID格式化

  • 线程ID类型为 pthread_t,通常是无符号长整型

  • 打印时使用格式:%lu(unsigned long int)

3.3 线程退出:pthread_exit

void pthread_exit(void *retval);

功能:线程自行退出,不返回调用者

参数

  • retval:线程退出时的返回值("临死遗言")

与exit()的区别

  • pthread_exit()只退出当前线程

  • exit()退出整个进程

示例

void* thread_func(void* arg) {
    int* result = malloc(sizeof(int));
    *result = 100;
    
    // 方式1:使用pthread_exit退出
    pthread_exit(result);
    
    // 方式2:使用return退出
    // return result;
}

3.4 线程取消:pthread_cancel

int pthread_cancel(pthread_t thread);

功能:请求结束指定的线程

参数

  • thread:要取消的线程ID

返回值

  • 成功:返回0

  • 失败:返回非0错误码

注意事项

  1. 被取消的线程需要有取消点(如sleep、read、write等系统调用)

  2. 线程可以设置取消状态,决定是否响应取消请求

  3. 线程被取消后,其资源需要被回收

示例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* thread_func(void* arg) {
    printf("线程开始执行,5秒后结束\n");
    
    // 设置线程可被取消
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    
    for (int i = 1; i <= 5; i++) {
        printf("线程运行中...%d秒\n", i);
        sleep(1);  // 取消点
    }
    
    printf("线程正常结束\n");
    return NULL;
}

int main() {
    pthread_t tid;
    
    pthread_create(&tid, NULL, thread_func, NULL);
    sleep(3);  // 主线程等待3秒
    
    // 取消子线程
    printf("主线程请求取消子线程\n");
    pthread_cancel(tid);
    
    // 等待线程结束
    pthread_join(tid, NULL);
    printf("子线程已被取消\n");
    
    return 0;
}

3.5 线程回收(阻塞方式):pthread_join

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

功能:阻塞等待指定线程结束,并回收其资源

参数

  • thread:要回收的线程ID

  • retval:接收线程返回值的指针

返回值

  • 成功:返回0

  • 失败:返回非0错误码

示例

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

void* thread_func(void* arg) {
    int* result = malloc(sizeof(int));
    *result = 42;  // 计算结果
    printf("子线程计算结果: %d\n", *result);
    pthread_exit(result);  // 退出并返回结果
}

int main() {
    pthread_t tid;
    int* thread_result;
    
    pthread_create(&tid, NULL, thread_func, NULL);
    
    // 阻塞等待线程结束,并获取返回值
    if (pthread_join(tid, (void**)&thread_result) == 0) {
        printf("主线程收到子线程返回值: %d\n", *thread_result);
        free(thread_result);  // 记得释放内存
    }
    
    return 0;
}

3.6 线程分离:pthread_detach

int pthread_detach(pthread_t thread);

功能:设置线程为分离状态,线程退出后系统自动回收资源

参数

  • thread:要设置的线程ID(通常是自己的ID)

返回值

  • 成功:返回0

  • 失败:返回非0错误码

关键特性

  1. 分离后的线程不能被pthread_join回收

  2. 线程退出后,系统自动回收栈空间

  3. 主线程无需关心分离线程的回收问题

示例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* thread_func(void* arg) {
    // 设置自己为分离状态
    pthread_detach(pthread_self());
    
    printf("分离线程开始执行\n");
    sleep(2);
    printf("分离线程结束,资源将被系统自动回收\n");
    
    return NULL;
}

int main() {
    pthread_t tid;
    
    pthread_create(&tid, NULL, thread_func, NULL);
    
    // 分离线程后,主线程不需要调用pthread_join
    sleep(1);
    printf("主线程继续执行其他任务\n");
    sleep(3);  // 等待分离线程结束
    
    printf("主线程结束\n");
    return 0;
}

四、线程编程综合示例

4.1 多线程计算示例

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

#define THREAD_COUNT 4
#define ARRAY_SIZE 1000000

int array[ARRAY_SIZE];
long long partial_sum[THREAD_COUNT] = {0};

// 线程函数:计算部分和
void* calculate_partial_sum(void* arg) {
    int thread_id = *(int*)arg;
    int start = thread_id * (ARRAY_SIZE / THREAD_COUNT);
    int end = (thread_id + 1) * (ARRAY_SIZE / THREAD_COUNT);
    
    printf("线程%d计算范围: %d ~ %d\n", thread_id, start, end - 1);
    
    for (int i = start; i < end; i++) {
        partial_sum[thread_id] += array[i];
    }
    
    printf("线程%d部分和: %lld\n", thread_id, partial_sum[thread_id]);
    return NULL;
}

int main() {
    pthread_t threads[THREAD_COUNT];
    int thread_ids[THREAD_COUNT];
    
    // 初始化数组
    for (int i = 0; i < ARRAY_SIZE; i++) {
        array[i] = i + 1;
    }
    
    // 创建多个线程
    for (int i = 0; i < THREAD_COUNT; i++) {
        thread_ids[i] = i;
        if (pthread_create(&threads[i], NULL, calculate_partial_sum, &thread_ids[i]) != 0) {
            perror("线程创建失败");
            return 1;
        }
    }
    
    // 等待所有线程结束
    for (int i = 0; i < THREAD_COUNT; i++) {
        pthread_join(threads[i], NULL);
    }
    
    // 合并结果
    long long total_sum = 0;
    for (int i = 0; i < THREAD_COUNT; i++) {
        total_sum += partial_sum[i];
    }
    
    printf("数组总和: %lld\n", total_sum);
    printf("预期总和: %lld\n", (long long)ARRAY_SIZE * (ARRAY_SIZE + 1) / 2);
    
    return 0;
}

4.2 线程安全退出模式

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

// 全局标志,用于通知线程退出
volatile int exit_flag = 0;

void* worker_thread(void* arg) {
    printf("工作线程启动\n");
    
    while (!exit_flag) {
        printf("工作线程执行任务...\n");
        sleep(1);
    }
    
    printf("工作线程收到退出信号,准备退出\n");
    return NULL;
}

void signal_handler(int sig) {
    if (sig == SIGINT) {
        printf("\n收到Ctrl+C信号,通知线程退出\n");
        exit_flag = 1;
    }
}

int main() {
    pthread_t tid;
    
    // 设置信号处理
    signal(SIGINT, signal_handler);
    
    // 创建工作线程
    if (pthread_create(&tid, NULL, worker_thread, NULL) != 0) {
        perror("线程创建失败");
        return 1;
    }
    
    printf("主线程运行中,按Ctrl+C退出程序\n");
    
    // 等待工作线程退出
    pthread_join(tid, NULL);
    
    printf("程序正常退出\n");
    return 0;
}

六、编译与运行

编译时需要链接pthread库:

# 编译命令
gcc -o thread_demo thread_demo.c -pthread

# 运行程序
./thread_demo

总结

本文全面介绍了Linux线程编程的核心概念和关键技术:

主题

核心内容

关键函数

线程概念

轻量级进程,共享资源,独立执行

-

线程创建

创建新线程执行指定函数

pthread_create

线程标识

获取当前线程ID

pthread_self

线程退出

线程主动退出并返回值

pthread_exit

线程取消

请求终止其他线程

pthread_cancel

线程回收

阻塞等待线程结束并回收资源

pthread_join

线程分离

设置线程自动回收

pthread_detach

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值