C语言中的多线程编程:POSIX线程库(Pthreads)入门与实战(二)

目录

一、Pthreads API概览

线程创建与管理

线程同步机制

线程局部存储(TLS)

二、Pthreads实战示例

简单多线程示例

互斥量同步示例

条件变量应用示例

线程局部存储实战


一、Pthreads API概览

线程创建与管理

pthread_create()函数详解

  • 参数
    • pthread_t *thread:指向新创建线程ID的指针,用于接收创建成功的线程标识符。
    • const pthread_attr_t *attr:指向线程属性对象的指针,可以指定线程的调度策略、优先级、栈大小等属性。若传入NULL,则使用默认属性。
    • void *(*start_routine)(void *):线程执行的起始函数,该函数接受一个void指针参数,并返回一个void指针。
    • void *arg:传递给线程起始函数的参数。
  • 返回值
    • 若成功创建线程,返回0;否则返回错误编号(如EINVAL、EAGAIN等)。
  • 错误处理
    • 应检查返回值,若非0,则使用perror()strerror()输出错误信息,或根据错误编号进行相应处理。
  • 示例
pthread_t thread_id;
int ret = pthread_create(&thread_id, NULL, worker_thread, (void *)&data);
if (ret != 0) {
    perror("pthread_create failed");
    exit(EXIT_FAILURE);
}

pthread_join()与pthread_detach():线程同步与资源回收

  • pthread_join()
    • 作用:阻塞调用线程,直到指定线程(通过线程ID指定)执行完毕,并获取其返回值(通过void **retval参数接收)。
    • 用途:确保线程执行结束并回收其资源,或者获取线程的执行结果。
    • 示例:
void *thread_return_value;
pthread_join(thread_id, &thread_return_value);

pthread_detach()

  • 作用:将指定线程标记为分离状态,使得该线程结束后其资源能自动回收,无需调用pthread_join()
  • 用途:适用于不关心线程返回值且希望减少资源管理复杂性的场景。
  • 示例:
pthread_detach(thread_id);

线程同步机制

互斥量(Mutexes)

  • 创建:使用pthread_mutex_init()函数,指定互斥量属性(可选)。
  • 锁定:使用pthread_mutex_lock()函数,阻塞当前线程直至获得互斥量所有权。
  • 解锁:使用pthread_mutex_unlock()函数,释放互斥量所有权,可能唤醒等待该互斥量的其他线程。
  • 属性设置:通过pthread_mutexattr_init()pthread_mutexattr_settype()等函数调整互斥量的属性,如是否为递归锁、是否为错误检查锁等。
  • 销毁:使用pthread_mutex_destroy()函数,释放互斥量占用的资源。

条件变量(Condition Variables)

  • 原理:条件变量用于线程间的同步,允许线程在某个条件不满足时阻塞自己,当其他线程改变了该条件并通知等待的线程后,被阻塞的线程才能继续执行。
  • 使用场景:生产者-消费者模型、工作队列、多线程资源池等。
  • 相关函数
    • pthread_cond_init():初始化条件变量。
    • pthread_cond_wait():阻塞当前线程,直到收到信号或超时。
    • pthread_cond_signal():唤醒一个等待该条件变量的线程。
    • pthread_cond_broadcast():唤醒所有等待该条件变量的线程。
    • pthread_cond_destroy():销毁条件变量。

其他同步原语(如读写锁、屏障等)简介

  • 读写锁(pthread_rwlock_t):允许多个读线程同时访问资源,但写线程访问时会排斥所有读写线程。适用于读多写少的场景。
  • 屏障(pthread_barrier_t):让一组线程在某个点阻塞,直到所有线程都达到该点才同时解除阻塞,常用于多线程间的同步点控制。

线程局部存储(TLS)

pthread_key_create()与pthread_setspecific():创建与访问线程特定数据

  • pthread_key_create():创建一个线程特定数据键,用于关联线程特定数据。
  • pthread_setspecific():为当前线程在指定键下设置线程特定数据。
  • pthread_getspecific():获取当前线程在指定键下的线程特定数据。
  • 应用场景
    • 避免全局数据的竞争:将原本全局的数据改为线程局部存储,每个线程有自己的副本,避免了数据竞争。
    • 简化资源管理:线程结束时,与线程特定数据关联的清理函数会被自动调用,简化了资源的生命周期管理。

综上所述,Pthreads API提供了丰富的线程创建、管理、同步功能,包括线程创建与管理函数(如pthread_create()pthread_join()pthread_detach())、线程同步原语(如互斥量、条件变量、读写锁等),以及线程局部存储机制,为编写高效、安全的多线程C程序提供了坚实基础。

二、Pthreads实战示例

简单多线程示例

下面的示例创建了3个计算任务线程,每个线程执行一个简单的计算任务并打印结果,最后主线程等待所有子线程完成。

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

void *compute_task(void *arg) {
    int task_id = (int)arg;
    int result = task_id * task_id;
    printf("Task %d computed result: %d\n", task_id, result);
    pthread_exit(NULL);  // 线程退出
}

int main() {
    pthread_t threads[3];
    int task_ids[] = {1, 2, 3};

    for (int i = 0; i < 3; ++i) {
        int rc = pthread_create(&threads[i], NULL, compute_task, (void *)&task_ids[i]);
        if (rc) {
            perror("pthread_create failed");
            exit(EXIT_FAILURE);
        }
    }

    for (int i = 0; i < 3; ++i) {
        pthread_join(threads[i], NULL);
    }

    printf("All tasks completed.\n");
    return 0;
}

互斥量同步示例

下面的示例实现了一个线程安全计数器,使用互斥量保护对全局计数器的访问,避免数据竞争。

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

pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
int global_counter = 0;

void *increment_counter(void *arg) {
    int iterations = (int)arg;
    for (int i = 0; i < iterations; ++i) {
        pthread_mutex_lock(&counter_mutex);
        global_counter++;
        pthread_mutex_unlock(&counter_mutex);
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[2];

    pthread_create(&threads[0], NULL, increment_counter, (void *)100000);
    pthread_create(&threads[1], NULL, increment_counter, (void *)100000);

    pthread_join(threads[0], NULL);
    pthread_join(threads[1], NULL);

    printf("Final counter value: %d\n", global_counter);
    return 0;
}

条件变量应用示例

下面的示例实现了一个简单的生产者-消费者模型,使用条件变量协调生产者线程与消费者线程之间的协作。

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

#define BUFFER_SIZE 5
#define NUM_ITEMS 10

pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;
int buffer[BUFFER_SIZE];
int in = 0, out = 0, count = 0;

void *producer(void *arg) {
    int item;
    for (int i = 0; i < NUM_ITEMS; ++i) {
        item = rand() % 100;  // 生成随机数作为生产物品

        pthread_mutex_lock(&buffer_mutex);
        while (count == BUFFER_SIZE) {  // 当缓冲区满时,生产者等待
            pthread_cond_wait(&not_full, &buffer_mutex);
        }
        buffer[in++] = item;
        in %= BUFFER_SIZE;
        count++;
        pthread_cond_signal(&not_empty);  // 生产物品后,唤醒消费者
        pthread_mutex_unlock(&buffer_mutex);
    }
    pthread_exit(NULL);
}

void *consumer(void *arg) {
    int item;
    for (int i = 0; i < NUM_ITEMS; ++i) {
        pthread_mutex_lock(&buffer_mutex);
        while (count == 0) {  // 当缓冲区空时,消费者等待
            pthread_cond_wait(&not_empty, &buffer_mutex);
        }
        item = buffer[out++];
        out %= BUFFER_SIZE;
        count--;
        pthread_cond_signal(&not_full);  // 消费物品后,唤醒生产者
        pthread_mutex_unlock(&buffer_mutex);
        printf("Consumed item: %d\n", item);
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t producer_thread, consumer_thread;

    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);

    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    return 0;
}

线程局部存储实战

下面的示例利用线程局部存储(TLS)存储每个线程的特定状态(线程ID),展示其在复杂多线程环境中的优势。

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

pthread_key_t thread_id_key;
pthread_once_t thread_id_key_once = PTHREAD_ONCE_INIT;

void init_thread_id_key() {
    pthread_key_create(&thread_id_key, NULL);
}

void *print_thread_id(void *arg) {
    pthread_once(&thread_id_key_once, init_thread_id_key);
    pthread_setspecific(thread_id_key, (void *)pthread_self());

    while (1) {
        printf("Thread ID (from TLS): %lu\n", (unsigned long)pthread_getspecific(thread_id_key));
        sleep(1);
    }
}

int main() {
    pthread_t threads[3];

    for (int i = 0; i < 3; ++i) {
        pthread_create(&threads[i], NULL, print_thread_id, NULL);
    }

    for (int i = 0; i < 3; ++i) {
        pthread_join(threads[i], NULL);
    }

    return 0;
}

以上四个示例分别展示了Pthreads在创建多线程、使用互斥量进行同步、应用条件变量协调线程协作,以及利用线程局部存储存储线程特定状态方面的实战应用。通过这些示例,可以更好地理解和掌握Pthreads在多线程编程中的实际应用。

  • 12
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JJJ69

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值