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

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

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

相关推荐
無限進步D3 小时前
Java 运行原理
java·开发语言·入门
是苏浙4 小时前
JDK17新增特性
java·开发语言
SPC的存折5 小时前
1、Redis数据库基础
linux·运维·服务器·数据库·redis·缓存
爱学习的小囧6 小时前
VMware ESXi 6.7U3v 新版特性、驱动集成教程和资源包、部署教程及高频问答详情
运维·服务器·虚拟化·esxi6.7·esxi蟹卡驱动
小疙瘩6 小时前
只是记录自己发布若依分离系统到linux过程中遇到的问题
linux·运维·服务器
dldw7777 小时前
IE无法正常登录windows2000server的FTP服务器
运维·服务器·网络
阿里加多7 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood7 小时前
java中`==`和`.equals()`区别
java·开发语言·python
我是伪码农7 小时前
外卖餐具智能推荐
linux·服务器·前端
zs宝来了8 小时前
AQS详解
java·开发语言·jvm