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

发布时间:2024年01月03日

介绍

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

  1. 互斥锁 (Mutexes)

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

    • pthread_rwlock_t 允许多个线程同时进行读取操作,但在写入操作时会排斥所有其他读写线程。
    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 与互斥锁结合使用,允许线程阻塞等待特定条件变为真。
    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 也可以用于同步线程,它是一个计数器,控制可以同时访问资源的线程数量。
    sem_t semaphore;
    sem_init(&semaphore, 0, 1); // 初始化为1,即一次只允许一个线程通过
    
    sem_wait(&semaphore); // 阻塞直到信号量计数大于0并减一
    // 执行临界区代码
    sem_post(&semaphore); // 信号量加一,释放资源
    

应用举例

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

#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保护对共享资源(缓冲区和缓冲区计数器)的访问,确保了线程间的同步。

总结

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

文章来源:https://blog.csdn.net/scy518/article/details/135354388
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。