Linux线程的共享资源与非共享资源详解

Linux线程的共享资源与非共享资源详解

  • [1. 线程基础概念回顾](#1. 线程基础概念回顾)
  • [2. 线程共享资源](#2. 线程共享资源)
    • [2.1 内存相关资源](#2.1 内存相关资源)
    • [2.2 进程属性相关资源](#2.2 进程属性相关资源)
  • [3. 线程非共享资源](#3. 线程非共享资源)
    • [3.1 线程私有资源](#3.1 线程私有资源)
    • [3.2 线程局部存储(TLS)](#3.2 线程局部存储(TLS))
  • [4. 共享资源同步问题](#4. 共享资源同步问题)
    • [4.1 典型问题:竞态条件](#4.1 典型问题:竞态条件)
    • [4.2 同步解决方案](#4.2 同步解决方案)
  • [5. 实际应用案例](#5. 实际应用案例)
    • [5.1 Web服务器设计](#5.1 Web服务器设计)
    • [5.2 生产者-消费者模型](#5.2 生产者-消费者模型)
  • [6. 性能考量](#6. 性能考量)
    • [6.1 共享资源访问开销](#6.1 共享资源访问开销)
    • [6.2 优化建议](#6.2 优化建议)
  • [7. 总结](#7. 总结)

1. 线程基础概念回顾

在Linux系统中,线程是程序执行的最小单位,也被称为轻量级进程(LWP)。与进程相比,线程创建、销毁和切换的开销更小,因为它们共享许多资源。Linux通过NPTL(Native POSIX Thread Library)实现线程,它使用clone()系统调用来创建线程。
进程 代码段 数据段 堆 打开的文件 信号处理器 线程1 线程2 线程ID 寄存器 栈 线程ID 寄存器 栈

2. 线程共享资源

2.1 内存相关资源

共享资源

  • 代码段:所有线程执行相同的程序代码
  • 数据段:全局变量和静态变量
  • 堆内存:动态分配的内存(malloc/new)
  • 文件描述符表:打开的文件、套接字等
  • 信号处理程序:信号处理器是进程级别的
c 复制代码
#include <pthread.h>
#include <stdio.h>

int global_var = 0;  // 共享变量

void* thread_func(void* arg) {
    global_var++;  // 所有线程共享访问
    printf("Thread %ld: global_var = %d\n", (long)arg, global_var);
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, thread_func, (void*)1);
    pthread_create(&t2, NULL, thread_func, (void*)2);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("Final global_var = %d\n", global_var);
    return 0;
}

2.2 进程属性相关资源

共享资源

  • 进程ID和父进程ID
  • 进程组ID和会话ID
  • 用户ID和组ID
  • 当前工作目录
  • 文件模式创建掩码(umask)
共享资源类别 具体内容 线程影响
内存资源 代码段、数据段、堆 线程可直接访问和修改
文件资源 文件描述符表 线程共享文件偏移量
进程属性 用户/组ID、工作目录 线程继承这些属性

3. 线程非共享资源

3.1 线程私有资源

非共享资源

  • 线程ID:每个线程有唯一标识符
  • 寄存器状态:包括程序计数器PC
  • 栈空间:每个线程有独立的调用栈
  • 信号掩码:可以设置不同的阻塞信号
  • 线程特定数据(TSD) :pthread_key_create创建
  • errno变量:每个线程有自己的errno

Thread1 独立栈 Thread2 独立栈 Thread3 独立栈 共享堆

3.2 线程局部存储(TLS)

C11和GCC提供了_Thread_local关键字,POSIX有pthread_key_t

c 复制代码
#include <pthread.h>
#include <stdio.h>

__thread int tls_var = 0;  // 每个线程有独立副本

void* thread_func(void* arg) {
    tls_var += (long)arg;
    printf("Thread %ld: tls_var = %d\n", (long)arg, tls_var);
    return NULL;
}

int main() {
    pthread_t t1, t2;
    tls_var = 10;
    pthread_create(&t1, NULL, thread_func, (void*)1);
    pthread_create(&t2, NULL, thread_func, (void*)2);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("Main thread tls_var = %d\n", tls_var);
    return 0;
}

4. 共享资源同步问题

4.1 典型问题:竞态条件

当多个线程同时访问共享资源且至少有一个线程修改资源时,如果没有正确同步,会导致不确定的结果。

案例:银行账户转账问题

c 复制代码
int balance = 1000;  // 共享账户余额

void* withdraw(void* arg) {
    int amount = *(int*)arg;
    if (balance >= amount) {
        sleep(1);  // 模拟处理延迟
        balance -= amount;
    }
    return NULL;
}

// 两个线程同时取款800,可能导致余额为负数

4.2 同步解决方案

1. 互斥锁(Mutex)

c 复制代码
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* safe_withdraw(void* arg) {
    int amount = *(int*)arg;
    pthread_mutex_lock(&mutex);
    if (balance >= amount) {
        sleep(1);
        balance -= amount;
    }
    pthread_mutex_unlock(&mutex);
    return NULL;
}

2. 读写锁

c 复制代码
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

// 读操作
pthread_rwlock_rdlock(&rwlock);
// 读取共享数据
pthread_rwlock_unlock(&rwlock);

// 写操作
pthread_rwlock_wrlock(&rwlock);
// 修改共享数据
pthread_rwlock_unlock(&rwlock);

5. 实际应用案例

5.1 Web服务器设计

典型的多线程Web服务器架构:
接受连接 接受连接 接受连接 主线程 工作线程1 工作线程2 工作线程3 共享资源: 缓存/会话数据

共享资源

  • 监听套接字(主线程accept后传递给工作线程)
  • 内存缓存(如Redis数据)
  • 会话状态
  • 日志文件

非共享资源

  • 每个线程的处理套接字
  • 线程本地缓冲区
  • 请求处理状态

5.2 生产者-消费者模型

c 复制代码
#define BUFFER_SIZE 10

int buffer[BUFFER_SIZE];
int count = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_producer = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_consumer = PTHREAD_COND_INITIALIZER;

void* producer(void* arg) {
    for (int i = 0; i < 20; i++) {
        pthread_mutex_lock(&mutex);
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&cond_producer, &mutex);
        }
        buffer[count++] = i;
        pthread_cond_signal(&cond_consumer);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

void* consumer(void* arg) {
    for (int i = 0; i < 20; i++) {
        pthread_mutex_lock(&mutex);
        while (count == 0) {
            pthread_cond_wait(&cond_consumer, &mutex);
        }
        printf("Consumed: %d\n", buffer[--count]);
        pthread_cond_signal(&cond_producer);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

6. 性能考量

6.1 共享资源访问开销

操作类型 相对开销 说明
线程本地访问 1x 访问栈变量最快
无竞争共享访问 1.2x 访问全局变量稍慢
互斥锁保护访问 10-100x 涉及系统调用和可能的上下文切换
条件变量等待 100-1000x 线程挂起和唤醒开销大

6.2 优化建议

  1. 减少共享数据:尽可能使用线程局部存储
  2. 缩小临界区:只锁定必要的代码段
  3. 读写分离:使用读写锁替代互斥锁
  4. 无锁编程:在适当场景使用原子操作
  5. 线程局部缓存:减少对共享数据的直接访问

7. 总结

理解Linux线程的共享与非共享资源对于编写高效、安全的多线程程序至关重要。关键点总结:

  • 共享资源:内存空间、文件描述符、进程属性等
  • 非共享资源:栈、寄存器、线程特定数据等
  • ⚠️ 同步必要:对共享资源的并发访问必须正确同步
  • 🚀 性能优化:减少共享、缩小锁粒度、使用高效同步原语

通过合理设计线程间的资源共享与隔离,可以构建出既高效又可靠的多线程应用程序。

相关推荐
Promise4852 小时前
关于使用wsl实现linux移植(imux6ull)的网络问题
linux·服务器·网络
郝学胜-神的一滴2 小时前
Linux进程与线程的区别:从内存三级映射角度深入解析
linux·服务器·c++·程序人生
默凉2 小时前
c++使用http发送图像
开发语言·c++·http
不爱吃糖的程序媛2 小时前
OpenHarmony PC 第三方 C/C++ 库适配完整指南
c语言·c++·harmonyos
雪花凌落的盛夏2 小时前
x86电脑安装steamOS
linux
雪域迷影2 小时前
nlohmann::json库对象和json结构体转换的新方式
c++·json·nlohmann_json库
木千2 小时前
Qt中关于QLineEdit控件的editingFinished信号执行两次的处理方式
开发语言·qt
浔川python社2 小时前
C++小程序编写系列(2)
c++·算法·图论
YJlio2 小时前
ZoomIt 学习笔记(11.10):键入模式——在桌面上直接打字讲解的最佳实践
服务器·笔记·学习