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线程的共享与非共享资源对于编写高效、安全的多线程程序至关重要。关键点总结:

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

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

相关推荐
啊吧怪不啊吧9 小时前
UU远程协助迎来升级!第一期更新实测
运维·服务器·远程工作
Boilermaker199215 小时前
[Java 并发编程] Synchronized 锁升级
java·开发语言
MM_MS15 小时前
Halcon变量控制类型、数据类型转换、字符串格式化、元组操作
开发语言·人工智能·深度学习·算法·目标检测·计算机视觉·视觉检测
C_心欲无痕15 小时前
ts - tsconfig.json配置讲解
linux·前端·ubuntu·typescript·json
꧁Q༒ོγ꧂15 小时前
LaTeX 语法入门指南
开发语言·latex
njsgcs15 小时前
ue python二次开发启动教程+ 导入fbx到指定文件夹
开发语言·python·unreal engine·ue
alonewolf_9915 小时前
JDK17新特性全面解析:从语法革新到模块化革命
java·开发语言·jvm·jdk
冰西瓜60016 小时前
国科大2025操作系统高级教程期末回忆版
linux
古城小栈16 小时前
Rust 迭代器产出的引用层数——分水岭
开发语言·rust
ghie909016 小时前
基于MATLAB的TLBO算法优化实现与改进
开发语言·算法·matlab