问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

C语言多线程同步方法详解:互斥锁、条件变量、读写锁和信号量

创作时间:
作者:
@小白创作中心

C语言多线程同步方法详解:互斥锁、条件变量、读写锁和信号量

引用
1
来源
1.
https://docs.pingcode.com/baike/1243638

在多线程编程中,同步机制是确保数据一致性和程序正确性的关键。本文将详细介绍C语言中常用的四种多线程同步方法:互斥锁、条件变量、读写锁和信号量。通过具体的代码示例,帮助读者深入理解这些同步机制的原理和应用场景。


C语言多线程同步的方法包括:互斥锁、条件变量、读写锁、信号量。其中互斥锁是最常用且最基础的一种方法,它通过保证同一时刻只有一个线程能够访问共享资源,从而避免数据竞争和不一致的问题。互斥锁非常适合用于保护临界区,即那些多线程同时访问可能导致数据错误的代码部分。

一、互斥锁

互斥锁(Mutex)是多线程同步的基础工具,能有效防止数据竞争。它的基本操作包括锁定和解锁。锁定某一资源时,其他线程无法访问该资源,直到解锁。

1.1、互斥锁的基本概念

互斥锁提供了一种机制,使同一时间只有一个线程可以进入临界区。临界区是指那些访问共享资源的代码段。互斥锁通常通过pthread库提供的相关函数来实现。

1.2、互斥锁的使用示例

在C语言中,通过pthread库可以使用互斥锁,以下是一个简单的例子:

#include <pthread.h>
#include <stdio.h>  
#include <stdlib.h>  
pthread_mutex_t lock;  
int counter = 0;  
void* increment(void* arg) {  
    for (int i = 0; i < 100000; i++) {  
        pthread_mutex_lock(&lock);  
        counter++;  
        pthread_mutex_unlock(&lock);  
    }  
    return NULL;  
}  
int main() {  
    pthread_t t1, t2;  
    pthread_mutex_init(&lock, NULL);  
    pthread_create(&t1, NULL, increment, NULL);  
    pthread_create(&t2, NULL, increment, NULL);  
    pthread_join(t1, NULL);  
    pthread_join(t2, NULL);  
    pthread_mutex_destroy(&lock);  
    printf("Final counter value: %dn", counter);  
    return 0;  
}  

这个示例展示了如何使用互斥锁来保护共享变量counter,确保多个线程同时操作时不会出现数据竞争。

二、条件变量

条件变量(Condition Variable)提供了一种线程间的等待/通知机制。它允许线程在满足某些条件之前等待,并在条件满足时通知等待的线程。

2.1、条件变量的基本概念

条件变量与互斥锁结合使用,通常用于实现复杂的同步场景,例如生产者-消费者问题。线程可以在条件不满足时等待,当条件满足时被唤醒。

2.2、条件变量的使用示例

以下是一个使用条件变量的生产者-消费者模型的简单示例:

#include <pthread.h>
#include <stdio.h>  
#include <stdlib.h>  
#define BUFFER_SIZE 10  
int buffer[BUFFER_SIZE];  
int count = 0;  
pthread_mutex_t lock;  
pthread_cond_t not_full;  
pthread_cond_t not_empty;  
void* producer(void* arg) {  
    for (int i = 0; i < 100; i++) {  
        pthread_mutex_lock(&lock);  
        while (count == BUFFER_SIZE) {  
            pthread_cond_wait(&not_full, &lock);  
        }  
        buffer[count++] = i;  
        pthread_cond_signal(&not_empty);  
        pthread_mutex_unlock(&lock);  
    }  
    return NULL;  
}  
void* consumer(void* arg) {  
    for (int i = 0; i < 100; i++) {  
        pthread_mutex_lock(&lock);  
        while (count == 0) {  
            pthread_cond_wait(&not_empty, &lock);  
        }  
        int item = buffer[--count];  
        pthread_cond_signal(&not_full);  
        pthread_mutex_unlock(&lock);  
        printf("Consumed: %dn", item);  
    }  
    return NULL;  
}  
int main() {  
    pthread_t prod, cons;  
    pthread_mutex_init(&lock, NULL);  
    pthread_cond_init(&not_full, NULL);  
    pthread_cond_init(&not_empty, NULL);  
    pthread_create(&prod, NULL, producer, NULL);  
    pthread_create(&cons, NULL, consumer, NULL);  
    pthread_join(prod, NULL);  
    pthread_join(cons, NULL);  
    pthread_mutex_destroy(&lock);  
    pthread_cond_destroy(&not_full);  
    pthread_cond_destroy(&not_empty);  
    return 0;  
}  

这个示例展示了如何使用条件变量来解决生产者-消费者问题,确保生产者不会在缓冲区满时继续生产,消费者不会在缓冲区空时继续消费。

三、读写锁

读写锁(Read-Write Lock)允许多个线程同时读取共享资源,但在写入时必须独占锁。读写锁提高了并发性,适用于读操作频繁而写操作较少的场景。

3.1、读写锁的基本概念

读写锁提供了两种锁定方式:读锁和写锁。多个线程可以同时获取读锁,但写锁是独占的。在读多写少的情况下,读写锁能够显著提高性能。

3.2、读写锁的使用示例

以下是一个使用读写锁的简单示例:

#include <pthread.h>
#include <stdio.h>  
#include <stdlib.h>  
pthread_rwlock_t rwlock;  
int shared_data = 0;  
void* reader(void* arg) {  
    for (int i = 0; i < 10; i++) {  
        pthread_rwlock_rdlock(&rwlock);  
        printf("Reader %d: %dn", *((int*)arg), shared_data);  
        pthread_rwlock_unlock(&rwlock);  
        sleep(1);  
    }  
    return NULL;  
}  
void* writer(void* arg) {  
    for (int i = 0; i < 10; i++) {  
        pthread_rwlock_wrlock(&rwlock);  
        shared_data++;  
        printf("Writer: %dn", shared_data);  
        pthread_rwlock_unlock(&rwlock);  
        sleep(2);  
    }  
    return NULL;  
}  
int main() {  
    pthread_t r1, r2, w;  
    pthread_rwlock_init(&rwlock, NULL);  
    int id1 = 1, id2 = 2;  
    pthread_create(&r1, NULL, reader, &id1);  
    pthread_create(&r2, NULL, reader, &id2);  
    pthread_create(&w, NULL, writer, NULL);  
    pthread_join(r1, NULL);  
    pthread_join(r2, NULL);  
    pthread_join(w, NULL);  
    pthread_rwlock_destroy(&rwlock);  
    return 0;  
}  

这个示例展示了如何使用读写锁来保护共享数据,允许多个读者同时读取数据,但写者在写入时必须独占锁。

四、信号量

信号量(Semaphore)是一个更通用的同步工具,可以用来控制对共享资源的访问数量。信号量是一个计数器,表示可以同时访问资源的线程数。

4.1、信号量的基本概念

信号量有两个主要操作:等待(P操作)和信号(V操作)。等待操作会减少信号量,当信号量为零时,线程会阻塞。信号操作会增加信号量,当信号量大于零时,阻塞的线程会被唤醒。

4.2、信号量的使用示例

以下是一个使用信号量的简单示例:

#include <pthread.h>
#include <semaphore.h>  
#include <stdio.h>  
#include <stdlib.h>  
sem_t semaphore;  
int shared_data = 0;  
void* worker(void* arg) {  
    sem_wait(&semaphore);  
    shared_data++;  
    printf("Worker %d: %dn", *((int*)arg), shared_data);  
    sem_post(&semaphore);  
    return NULL;  
}  
int main() {  
    pthread_t t1, t2;  
    int id1 = 1, id2 = 2;  
    sem_init(&semaphore, 0, 1);  
    pthread_create(&t1, NULL, worker, &id1);  
    pthread_create(&t2, NULL, worker, &id2);  
    pthread_join(t1, NULL);  
    pthread_join(t2, NULL);  
    sem_destroy(&semaphore);  
    return 0;  
}  

这个示例展示了如何使用信号量来保护共享数据,确保同一时刻只有一个线程可以访问共享资源。

五、总结

在C语言中,实现多线程同步的方法有很多,包括互斥锁、条件变量、读写锁和信号量。每种方法都有其适用的场景和优缺点。

  • 互斥锁:适用于需要保护临界区的场景,简单且高效。
  • 条件变量:适用于需要线程等待特定条件的场景,如生产者-消费者模型。
  • 读写锁:适用于读操作多于写操作的场景,提高了并发性。
  • 信号量:适用于需要控制对共享资源访问数量的场景,更通用。

在实际应用中,选择合适的同步方法非常重要。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号