C语言多线程编程-线程同步

介绍

多线程编程,经常会遇到线程直接数据同步,为了保证数据访问安全,就必须考虑线程之间的同步问题。在C语言中,多线程编程的线程同步主要依赖于POSIX线程(Pthreads)库提供的同步原语。以下是一些关键的线程同步机制:

  1. 互斥锁 (Mutexes)

    • pthread_mutex_t 是一种互斥对象,用于保护共享资源,确保同一时间只有一个线程可以访问。
    c 复制代码
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
    pthread_mutex_lock(&mutex);       // 上锁
    // 访问共享资源的代码
    pthread_mutex_unlock(&mutex);     // 解锁
  2. 读写锁 (Read-Write Locks)

    • pthread_rwlock_t 允许多个线程同时进行读取操作,但在写入操作时会排斥所有其他读写线程。
    c 复制代码
    pthread_rwlock_t rwlock;
    pthread_rwlock_init(&rwlock, NULL);
    pthread_rwlock_rdlock(&rwlock);  // 读取锁定
    // 读取共享资源的代码
    pthread_rwlock_unlock(&rwlock);  // 释放锁定
    pthread_rwlock_wrlock(&rwlock);  // 写入锁定
    // 更新共享资源的代码
    pthread_rwlock_unlock(&rwlock);  // 释放锁定
  3. 条件变量 (Condition Variables)

    • 条件变量 pthread_cond_t 与互斥锁结合使用,允许线程阻塞等待特定条件变为真。
    c 复制代码
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    
    pthread_mutex_lock(&mutex);
    while (!condition_is_true()) { // 检查条件
      pthread_cond_wait(&cond, &mutex); // 条件不满足则等待
    }
    // 当条件满足时执行相关操作
    pthread_mutex_unlock(&mutex);
    
    // 另一个线程改变条件后,可以唤醒等待的线程
    pthread_mutex_lock(&mutex);
    condition_make_true(); // 改变条件
    pthread_cond_signal(&cond); // 唤醒一个等待线程
    // 或者唤醒所有等待线程
    pthread_cond_broadcast(&cond);
    pthread_mutex_unlock(&mutex);
  4. 信号量 (Semaphores)

    • POSIX信号量 sem_t 也可以用于同步线程,它是一个计数器,控制可以同时访问资源的线程数量。
    c 复制代码
    sem_t semaphore;
    sem_init(&semaphore, 0, 1); // 初始化为1,即一次只允许一个线程通过
    
    sem_wait(&semaphore); // 阻塞直到信号量计数大于0并减一
    // 执行临界区代码
    sem_post(&semaphore); // 信号量加一,释放资源

应用举例

以下是一个使用互斥锁(Mutexes)和条件变量(Condition Variables)实现的简单生产者-消费者问题的例子。生产者线程生成数据并放入缓冲区,消费者线程从缓冲区取出数据处理。

c 复制代码
#include <pthread.h>
#include <stdio.h>

#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE];
int buffer_count = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t full = PTHREAD_COND_INITIALIZER;
pthread_cond_t empty = PTHREAD_COND_INITIALIZER;

void* producer(void* arg) {
    int i;
    for (i = 0; i < 20; i++) {
        pthread_mutex_lock(&mutex);

        while (buffer_count == BUFFER_SIZE) { // 如果缓冲区已满
            pthread_cond_wait(&full, &mutex); // 生产者等待
        }

        buffer[buffer_count++] = i; // 放入数据
        printf("Producer produced %d\n", i);

        pthread_cond_signal(&empty); // 唤醒消费者
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

void* consumer(void* arg) {
    int i;
    while (1) {
        pthread_mutex_lock(&mutex);

        while (buffer_count == 0) { // 如果缓冲区为空
            pthread_cond_wait(&empty, &mutex); // 消费者等待
        }

        int data = buffer[--buffer_count]; // 取出数据
        printf("Consumer consumed %d\n", data);

        pthread_cond_signal(&full); // 唤醒生产者
        pthread_mutex_unlock(&mutex);

        // 在这里可以处理消费的数据,这里为了简化直接输出
    }
    return 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);

    pthread_cond_destroy(&full);
    pthread_cond_destroy(&empty);
    pthread_mutex_destroy(&mutex);

    return 0;
}
示例说明

在这个例子中,创建了两个线程:一个生产者线程和一个消费者线程。生产者在缓冲区未满时产生数据,并通过条件变量full通知消费者;消费者在缓冲区非空时消费数据,并通过条件变量empty通知生产者。通过互斥锁mutex保护对共享资源(缓冲区和缓冲区计数器)的访问,确保了线程间的同步。

总结

在使用这些同步机制时,重要的是要理解它们各自的适用场景和潜在的开销。不恰当的同步可能导致性能下降或死锁等问题。

相关推荐
aini_lovee21 分钟前
多目标粒子群优化(MOPSO)双适应度函数MATLAB实现
人工智能·算法·matlab
yong999029 分钟前
图像融合与拼接:完整MATLAB工具箱
算法·计算机视觉·matlab
春风不语50530 分钟前
深入理解主成分分析(PCA)
算法
apollowing32 分钟前
启发式算法WebApp实验室:从搜索策略到群体智能的能力进阶(二十二)
算法·启发式算法·web app
晚枫歌F36 分钟前
最小堆定时器
数据结构·算法
Lumos_7771 小时前
Linux -- 线程
java·jvm·算法
七颗糖很甜2 小时前
“十五五”气象发展规划:聚焦五大核心任务
大数据·python·算法
科研前沿2 小时前
镜像视界浙江科技有限公司的关键技术突破有哪些?
大数据·人工智能·科技·算法·音视频·空间计算
嫩萝卜头儿2 小时前
2 - 复杂度收尾 + 链表经典OJ
数据结构·算法·链表·复杂度
星马梦缘2 小时前
算法设计与分析 作业二 答案与解析
算法·图论·dfs·bfs·floyd-warshall·bellman_ford·多源最短路