多线程编程:线程私有与共享资源解析及线程操作函数详解


多线程编程:线程私有与共享资源解析及线程操作函数详解

一、线程的共享资源

共享资源是指被同一进程内所有线程共同访问的数据或资源。由于多个线程可能同时操作这些资源,必须通过同步机制(如互斥锁)确保线程安全。

1.1 常见的共享资源

  • 堆内存
    通过 mallocnew 动态分配的内存对所有线程可见。
    风险示例

    int *data = malloc(sizeof(int)); 
    *data = 42;
    
    // 线程A和线程B同时修改 data 的值
    

    若不使用锁保护,可能导致数据竞争(Data Race)。

  • 全局变量与静态变量
    存储在全局数据区的变量可被所有线程访问。

    static int counter = 0; // 所有线程共享
    
  • 文件描述符
    打开的文件、套接字等描述符共享,需注意多线程读写时的原子性。

    FILE *fp = fopen("log.txt", "w"); // 多个线程同时写入需同步
    
  • 代码段
    进程的代码(函数、指令)是只读的,线程可安全并发执行。

1.2 共享资源的管理

  • 互斥锁(Mutex)
    确保对共享资源的独占访问。
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_lock(&lock);
    counter++; // 临界区操作
    pthread_mutex_unlock(&lock);
    

二、线程的私有资源

私有资源是每个线程独立拥有的数据,其他线程无法直接访问。

2.1 常见的私有资源

  • 栈内存
    每个线程拥有独立的栈空间,存储局部变量和函数调用链。

    void thread_func() {
        int local_var = 10; // 栈变量,线程私有
    }
    
  • 线程局部存储(TLS)
    使用 __thread(GCC)或 thread_local(C11)声明线程私有变量。

    __thread int tls_var; // 每个线程独立副本
    
  • 寄存器状态
    程序计数器(PC)、栈指针(SP)等寄存器由线程独立维护。

2.2 TLS的典型应用

  • 避免全局变量竞争
    __thread int user_id; // 每个线程独立存储用户ID
    

三、线程操作函数详解

POSIX线程(pthread)库提供了丰富的线程管理接口。以下为核心函数解析。

3.1 线程创建:pthread_create

函数原型:

int pthread_create(
    pthread_t *thread,         // 线程ID
    const pthread_attr_t *attr,// 线程属性(通常为NULL)
    void *(*start_routine)(void *), // 线程入口函数
    void *arg                  // 传递给入口函数的参数
);

示例代码:

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

void* print_number(void *arg) {
    int num = *(int*)arg;
    printf("Received: %d\n", num);
    return NULL;
}

int main() {
    pthread_t tid;
    int value = 42;
    pthread_create(&tid, NULL, print_number, &value);
    pthread_join(tid, NULL);
    return 0;
}

参数传递陷阱:
直接传递栈变量地址可能导致数据竞争。改进方案:

// 动态分配参数内存
int *arg = malloc(sizeof(int));
*arg = 42;
pthread_create(&tid, NULL, print_number, arg);
// 在线程函数中释放内存

3.2 线程等待:pthread_join

函数原型:

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

作用:

  • 阻塞调用线程,直到目标线程结束。
  • 回收目标线程资源(如栈内存)。
  • 获取线程返回值(通过 retval 参数)。

示例:

void* thread_func(void *arg) {
    int *result = malloc(sizeof(int));
    *result = 100;
    return (void*)result;
}

int main() {
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);
    
    void *retval;
    pthread_join(tid, &retval);
    printf("Thread returned: %d\n", *(int*)retval);
    free(retval); // 释放返回值内存
    return 0;
}

错误处理:

  • ESRCH: 无效线程ID。
  • EINVAL: 线程不可连接(如已分离)。
  • EDEADLK: 试图等待自身导致死锁。

3.3 线程终止:pthread_exitpthread_self

pthread_exit:

void pthread_exit(void *retval); // 终止当前线程并返回 retval

关键区别:

  • return 语句仅退出线程函数,而 pthread_exit 直接终止线程。
  • 主线程调用 pthread_exit 后,程序会等待其他线程结束。

pthread_self:

pthread_t pthread_self(); // 获取当前线程ID

应用场景:

  • 日志记录时附加线程ID。
  • 调试多线程问题。

四、线程参数传递的最佳实践

4.1 避免共享栈变量

错误示例:

for (int i = 0; i < 5; i++) {
    pthread_create(&tid[i], NULL, func, &i); // 传递局部变量地址
}
// 子线程可能读取到被主线程修改后的 i

解决方案:

  • 动态分配内存

    int *arg = malloc(sizeof(int));
    *arg = i;
    pthread_create(&tid[i], NULL, func, arg);
    // 在线程函数中 free(arg)
    
  • 使用线程同步
    通过条件变量或屏障(Barrier)确保参数传递安全。


五、线程的生命周期管理

5.1 分离线程(Detached Thread)

  • 默认情况下线程是可连接的(Joinable),需手动调用 pthread_join
  • 分离线程(Detached)结束后自动释放资源。

设置分离属性:

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, func, NULL);
pthread_attr_destroy(&attr);

六、总结与建议

  1. 共享资源必须同步
    使用互斥锁、信号量或原子操作保护临界区。
  2. 优先使用线程局部存储(TLS)
    替代全局变量以减少竞争。
  3. 谨慎传递参数
    动态分配内存或复制数据避免悬垂指针。
  4. 及时回收资源
    调用 pthread_join 或设置分离属性防止资源泄漏。

附录:完整代码示例

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

#define NUM_THREADS 5

// 线程函数:动态分配参数内存
void* thread_func(void *arg) {
    int num = *(int*)arg;
    free(arg); // 释放参数内存
    printf("Thread %d started\n", num);
    // 模拟任务执行
    int *result = malloc(sizeof(int));
    *result = num * 10;
    pthread_exit(result); // 返回结果
}

int main() {
    pthread_t threads[NUM_THREADS];
    int *results[NUM_THREADS];

    // 创建线程并传递参数
    for (int i = 0; i < NUM_THREADS; i++) {
        int *arg = malloc(sizeof(int));
        *arg = i;
        if (pthread_create(&threads[i], NULL, thread_func, arg) != 0) {
            perror("pthread_create failed");
            exit(1);
        }
    }

    // 等待线程结束并收集结果
    for (int i = 0; i < NUM_THREADS; i++) {
        void *retval;
        if (pthread_join(threads[i], &retval) != 0) {
            perror("pthread_join failed");
            exit(1);
        }
        results[i] = (int*)retval;
        printf("Thread %d returned %d\n", i, *results[i]);
        free(results[i]); // 释放结果内存
    }

    return 0;
}

参考资料

  • POSIX Threads Programming: https://computing.llnl.gov/tutorials/pthreads/
  • 《UNIX环境高级编程》(Advanced Programming in the UNIX Environment)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值