【POSIX 线程库函数】

以下是关于 POSIX线程库(pthread) 的核心知识点总结,涵盖线程管理、同步机制及常见面试问题:


一、线程基础

1. 线程创建与终止
  • 创建线程pthread_create

    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
                       void *(*start_routine)(void *), void *arg);
    
    • thread:存储新线程的ID。
    • attr:线程属性(默认用NULL)。
    • start_routine:线程函数(返回void*,参数void*)。
    • arg:传递给线程函数的参数。

    示例

    void* print_hello(void *arg) {
        printf("Hello from thread!\n");
        return NULL;
    }
    pthread_t tid;
    pthread_create(&tid, NULL, print_hello, NULL);
    
  • 终止线程

    • 线程函数返回(隐式终止)。
    • 显式调用pthread_exit(void *retval)
    • 主线程调用exit()会终止整个进程(包括所有线程)。
2. 等待线程结束pthread_join
int pthread_join(pthread_t thread, void **retval);
  • 阻塞当前线程,直到目标线程结束。
  • retval:接收线程的返回值(需通过pthread_exitreturn传递)。

示例

void* thread_func(void *arg) {
    int *num = (int*)arg;
    *num *= 2;
    return (void*)num;
}
int main() {
    int val = 42;
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, &val);
    int *result;
    pthread_join(tid, (void**)&result);
    printf("Result: %d\n", *result); // 输出 84
}

二、线程同步机制

1. 互斥锁(Mutex)
  • 初始化与销毁

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态初始化
    // 或动态初始化
    pthread_mutex_init(&mutex, NULL);
    pthread_mutex_destroy(&mutex);
    
  • 加锁与解锁

    pthread_mutex_lock(&mutex);   // 阻塞直到获取锁
    // 临界区代码
    pthread_mutex_unlock(&mutex);
    

    示例(线程安全的计数器):

    int counter = 0;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    void* increment(void *arg) {
        for (int i = 0; i < 1000; i++) {
            pthread_mutex_lock(&mutex);
            counter++;
            pthread_mutex_unlock(&mutex);
        }
        return NULL;
    }
    
2. 条件变量(Condition Variables)
  • 初始化与销毁

    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    pthread_cond_init(&cond, NULL);
    pthread_cond_destroy(&cond);
    
  • 等待与通知

    // 等待条件(需配合互斥锁)
    pthread_cond_wait(&cond, &mutex);
    // 唤醒一个等待线程
    pthread_cond_signal(&cond);
    // 唤醒所有等待线程
    pthread_cond_broadcast(&cond);
    

    示例(生产者-消费者模型):

    int buffer = 0;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
    void* producer(void *arg) {
        pthread_mutex_lock(&mutex);
        buffer = 1;
        pthread_cond_signal(&cond); // 通知消费者
        pthread_mutex_unlock(&mutex);
        return NULL;
    }
    
    void* consumer(void *arg) {
        pthread_mutex_lock(&mutex);
        while (buffer == 0) {
            pthread_cond_wait(&cond, &mutex); // 释放锁并等待
        }
        printf("Consumed: %d\n", buffer);
        pthread_mutex_unlock(&mutex);
        return NULL;
    }
    
3. 读写锁(Read-Write Lock)
  • 适用场景:读操作频繁,写操作较少。
    pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
    pthread_rwlock_rdlock(&rwlock);    // 读锁
    pthread_rwlock_wrlock(&rwlock);    // 写锁
    pthread_rwlock_unlock(&rwlock);
    
4. 自旋锁(Spinlock)
  • 特点:忙等(不释放CPU),适用于锁持有时间短的场景。
    pthread_spinlock_t spinlock;
    pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
    pthread_spin_lock(&spinlock);
    pthread_spin_unlock(&spinlock);
    

三、线程属性

  • 设置线程属性(如分离状态、栈大小):
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    // 设置线程为分离状态(无需pthread_join)
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, thread_func, NULL);
    pthread_attr_destroy(&attr);
    

四、线程局部存储(Thread-Local Storage, TLS)

  • 用途:每个线程拥有独立的变量副本。
    pthread_key_t key;
    pthread_key_create(&key, NULL); // 第二个参数是析构函数
    // 设置线程局部变量
    pthread_setspecific(key, (void*)value);
    // 获取线程局部变量
    void* value = pthread_getspecific(key);
    

五、常见面试问题

  1. 线程 vs 进程的区别

    • 线程共享进程的内存空间,进程拥有独立的内存空间。
    • 线程切换开销小,进程切换开销大。
  2. 死锁的四个必要条件

    • 互斥条件、占有且等待、不可抢占、循环等待。
  3. 如何避免死锁

    • 按固定顺序加锁。
    • 使用超时锁(如pthread_mutex_trylock)。
  4. 竞争条件(Race Condition)

    • 多个线程同时访问共享资源且未同步,导致结果不确定性。

六、综合示例(多线程计算数组和)

#include 
#include 

#define NUM_THREADS 4
#define ARRAY_SIZE 1000

int array[ARRAY_SIZE];
int partial_sums[NUM_THREADS] = {0};

void* sum_array(void *arg) {
    int thread_id = *(int*)arg;
    int start = thread_id * (ARRAY_SIZE / NUM_THREADS);
    int end = start + (ARRAY_SIZE / NUM_THREADS);

    for (int i = start; i < end; i++) {
        partial_sums[thread_id] += array[i];
    }
    return NULL;
}

int main() {
    // 初始化数组
    for (int i = 0; i < ARRAY_SIZE; i++) array[i] = i + 1;

    pthread_t threads[NUM_THREADS];
    int thread_ids[NUM_THREADS];

    // 创建线程
    for (int i = 0; i < NUM_THREADS; i++) {
        thread_ids[i] = i;
        pthread_create(&threads[i], NULL, sum_array, &thread_ids[i]);
    }

    // 等待线程结束
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }

    // 汇总结果
    int total = 0;
    for (int i = 0; i < NUM_THREADS; i++) {
        total += partial_sums[i];
    }
    printf("Total sum: %d\n", total); // 应输出 500500
    return 0;
}

七、注意事项

  1. 编译时链接pthread库
    gcc program.c -o program -lpthread
    
  2. 避免内存泄漏:确保销毁动态初始化的锁和条件变量。
  3. 线程安全函数:确保调用的库函数是线程安全的(如rand_r替代rand)。

你可能感兴趣的:(算法,linux,嵌入式,c语言,嵌入式软件)