Posts 聊聊多线程(一)
Post
Cancel

聊聊多线程(一)

1. 线程比较

典型用法

1
2
3
4
5
6
7
8
9
10
pthread_t main_thread = pthread_self();  // 获取主线程ID

void* worker_thread(void* arg) {
    if (pthread_equal(pthread_self(), main_thread)) {
        printf("This is the main thread\n");
    } else {
        printf("This is a worker thread\n");
    }
    return NULL;
}

2. 线程创建

1
2
int pthread_create(pthread_t *thread, const pthread_t *attr,
                   void *(*start_routine)(void *), void *arg);

​1. ​执行顺序不确定性​​

  • 新线程与主线程的执行顺序由系统调度决定,无法预先确定
  • 必须通过同步机制(如互斥锁、条件变量)控制执行流程 ​​2. 资源共享特性​​
  • 所有线程共享进程的全局变量和堆内存
  • 需要特别注意线程安全问题,避免数据竞争
  • 示例:多个线程同时修改全局变量需加锁保护 ​​3. 属性继承规则​​
  • 继承调用线程的浮点运算环境
  • 继承信号屏蔽字(Signal Mask)
  • 不继承挂起信号(Pending Signals)
    1. ​​资源管理要求​​
  • 必须显式回收线程资源(pthread_join)
  • 或设置为分离线程(pthread_detach)
  • 避免线程资源泄漏

3. 线程终止

线程退出的三种方式

1. 从启动例程返回

​​实现方式​​:线程函数执行到return语句正常退出

1
2
3
4
void* thread_func(void* arg) {
    // ...线程逻辑...
    return (void*)42; // 返回退出码
}

2. 被其他线程取消

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
调用线程:pthread_cancel(thread_id)
目标线程需设置可取消状态(默认启用)
异步终止可能引发资源泄漏
应在取消点(如pthread_testcancel())安全退出
*/

// 线程A取消线程B
pthread_cancel(thread_b);

// 线程B的可取消逻辑
void* thread_func(void* arg) {
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
    while(1) {
        pthread_testcancel(); // 安全检查点
        // ...工作逻辑...
    }
}

3. 调用pthread_exit

1
2
3
4
5
6
7
8
9
10
11
12
/*
可在线程任意位置退出
退出码可由pthread_join获取
主线程调用时不会终止进程(与exit区别)
*/

void* thread_func(void* arg) {
    if(error_occurred) {
        pthread_exit((void*)-1); // 异常退出
    }
    // ...正常逻辑...
}

常见api

每个 pthread_create 应对应 pthread_join 或 pthread_detach,避免僵尸线程。

pthread_exit

1
2
3
4
5
6
7
8
9
10
11
12
// 1. 显式终止当前线程,并返回一个退出值(retval)。
pthread_exit(void *retval)

// 2. 仅终止调用线程,不影响其他线程

// 3. retval 可被其他线程通过 pthread_join 获取

// 4. 与 exit() 的区别​​:
pthread_exit(NULL);  // 仅终止当前线程
exit(0);             // 终止整个进程(包括所有线程)

// 5. 若主线程调用 pthread_exit,进程会保持运行直到所有线程结束。

pthread_join

1
2
3
4
5
6
// 1. 阻塞等待目标线程结束,并获取其退出值。
pthread_join(pthread_t thread, void **retval)

void *exit_code;
pthread_join(thread_id, &exit_code);
printf("线程退出码:%ld\n", (long)exit_code);

pthread_cancel

线程清理处理程序(Thread Cleanup Handlers)

pthread_cleanup_push/pthread_cleanup_pop 提供线程资源清理的栈式管理,类似C++的RAII机制,确保:

  • 线程异常退出时自动释放资源
  • 支持嵌套的清理处理程序
1
2
3
pthread_cleanup_push(rtn, arg);  // 注册清理函数
/* 临界区代码 */
pthread_cleanup_pop(execute);   // 注销清理函数

触发的三种方式:

  1. ​​线程被取消时(pthread_cancel)​
  2. ​​线程主动调用pthread_exit​
  3. ​​执行pthread_cleanup_pop(非零参数)​

请注意, 调用顺序与注册顺序相反

1
2
3
4
5
6
7
8
9
10
11
12
// 举例说明

void* thread_func(void* arg) {
    char *buf = malloc(1024); // 危险资源
    pthread_cleanup_push(free, buf); // 注册保险
    
    // 使用buf(可能出错或被取消)
    if (error) pthread_exit(NULL);
    
    pthread_cleanup_pop(1); // 正常退出也free  非0	执行清理函数并移除
    return NULL;
}

pthread_detach

1
2
3
// 1. 将指定线程标记为​​分离状态(detached state)​​,使该线程终止时系统自动回收其资源,无需其他线程调用pthread_join。
// 2. 不可用 pthread_join 回收已经分离的线程, 会产生未定义行为
int pthread_detach(pthread_t thread);

4. 线程同步

互斥量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
       #include <pthread.h>

        // 初始化
       int pthread_mutex_destroy(pthread_mutex_t *mutex);
       int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);


       int pthread_mutex_lock(pthread_mutex_t *mutex);
       int pthread_mutex_trylock(pthread_mutex_t *mutex);
       int pthread_mutex_unlock(pthread_mutex_t *mutex);

        // 超时自动释放
       int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
           const struct timespec *restrict abstime);

避免死锁

  1. 顺序锁
  2. try_lock, 未获取锁则释放自身持有的锁
  3. 超时机制 死锁检测等

读写锁

1
2
3
4
xm@hcss-ecs-4208:/tmp/tmp.17PXDZ8IS3$ man pthread_rwlock
pthread_rwlock_destroy         pthread_rwlock_wrlock
pthread_rwlock_rdlock          pthread_rwlock_unlock
pthread_rwlock_timedrdlock     pthread_rwlock_timedwrlock

条件变量

1
2
3
4
5
// 常见api
pthread_cond_init
pthread_cond_wait
pthread_cond_signal
pthread_cond_destroy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 举例说明
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int shared_data = 0;  // 共享数据

// 等待线程(消费者)
void* consumer(void* arg) {
    pthread_mutex_lock(&mutex);
    while (shared_data == 0) {  // 必须用 while(避免虚假唤醒)
        pthread_cond_wait(&cond, &mutex);  // 先释放锁,再阻塞线程
                                           // 被唤醒后,会 ​​重新获取锁​​
    }
    printf("Consumed: %d\n", shared_data);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

// 通知线程(生产者)
void* producer(void* arg) {
    pthread_mutex_lock(&mutex);
    shared_data = 42;  // 修改共享数据
    pthread_cond_signal(&cond);  // 唤醒一个等待线程
    pthread_mutex_unlock(&mutex);
    return NULL;
}

信号量

1
略 pv 操作

自旋锁

1
2
3
4
5
6
7
8
pthread_spin_init()	初始化自旋锁
pthread_spin_destroy()	销毁自旋锁
pthread_spin_lock()	阻塞式获取锁(自旋等待)
pthread_spin_trylock()	非阻塞式尝试获取锁
pthread_spin_unlock()	释放锁

// 0 线程间共享    1 进程间共享
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);

** 为什么要用自旋锁, 不直接用pthread_mutex ?**

(1)避免上下文切换的开销​ (2)适用于极短临界区​ (3)多核 CPU 的优势​​

5. 屏障

1
屏障(Barrier)​​ 是一种同步机制,用于确保多个线程(或进程)在某个点上​​全部到达后​​才能继续执行。
1
2
3
pthread_barrier_init()	初始化屏障
pthread_barrier_wait()	线程等待屏障
pthread_barrier_destroy()	销毁屏障
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 举例说明
#include <stdio.h>
#include <pthread.h>

#define THREAD_NUM 4

pthread_barrier_t barrier;

void* task(void* arg) {
    int id = *(int*)arg;
    printf("Thread %d: Phase 1\n", id);
    
    // 模拟计算
    sleep(1);  

    // 等待所有线程完成 Phase 1
    pthread_barrier_wait(&barrier);

    // 只有所有线程到达后,才会执行 Phase 2
    printf("Thread %d: Phase 2\n", id);
    return NULL;
}

int main() {
    pthread_t threads[THREAD_NUM];
    int ids[THREAD_NUM];

    // 初始化屏障(等待 4 个线程)
    pthread_barrier_init(&barrier, NULL, THREAD_NUM);

    // 创建 4 个线程
    for (int i = 0; i < THREAD_NUM; i++) {
        ids[i] = i;
        pthread_create(&threads[i], NULL, task, &ids[i]);
    }

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

    // 销毁屏障
    pthread_barrier_destroy(&barrier);
    return 0;
}
This post is licensed under CC BY 4.0 by the author.

Contents

Trending Tags