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

C语言如何实现唤醒和阻塞操作

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

C语言如何实现唤醒和阻塞操作

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

C语言中的唤醒和阻塞操作是多线程编程中的重要概念,主要用于实现线程间的同步和通信。本文将详细介绍如何使用条件变量、互斥锁和信号量等机制来实现这些操作,并通过生产者-消费者问题和哲学家就餐问题等经典应用场景进行说明。

C语言实现唤醒和阻塞操作主要通过条件变量、互斥锁、信号量等机制来实现。这些机制允许线程在特定条件满足时进入等待状态,并在条件满足时被唤醒。以下是详细的实现方法。

一、条件变量

条件变量是一种同步机制,用于阻塞一个线程,直到某个特定条件为真。条件变量通常与互斥锁一起使用,以避免竞争条件。条件变量的主要函数包括pthread_cond_waitpthread_cond_signal

1. 条件变量初始化和销毁

在使用条件变量之前,需要先进行初始化。可以使用pthread_cond_init函数进行初始化。销毁条件变量则使用pthread_cond_destroy函数。

#include <pthread.h>

pthread_cond_t cond;  
pthread_mutex_t mutex;  

void init() {  
    pthread_cond_init(&cond, NULL);  
    pthread_mutex_init(&mutex, NULL);  
}  

void cleanup() {  
    pthread_cond_destroy(&cond);  
    pthread_mutex_destroy(&mutex);  
}  

2. 阻塞操作

当线程需要等待某个条件时,可以使用pthread_cond_wait函数。该函数会先自动释放互斥锁,然后进入等待状态,直到条件变量被唤醒。

void wait_for_condition() {
    pthread_mutex_lock(&mutex);  
    pthread_cond_wait(&cond, &mutex);  
    pthread_mutex_unlock(&mutex);  
}  

3. 唤醒操作

当条件满足时,可以使用pthread_cond_signalpthread_cond_broadcast函数唤醒一个或所有等待的线程。

void signal_condition() {
    pthread_mutex_lock(&mutex);  
    pthread_cond_signal(&cond); // 唤醒一个线程  
    pthread_mutex_unlock(&mutex);  
}  

void broadcast_condition() {  
    pthread_mutex_lock(&mutex);  
    pthread_cond_broadcast(&cond); // 唤醒所有线程  
    pthread_mutex_unlock(&mutex);  
}  

二、互斥锁

互斥锁(mutex)是一种用于保护共享资源的同步机制。互斥锁确保在同一时刻只有一个线程可以访问共享资源。互斥锁的主要函数包括pthread_mutex_lockpthread_mutex_unlock

1. 互斥锁初始化和销毁

互斥锁需要先进行初始化,可以使用pthread_mutex_init函数。销毁互斥锁则使用pthread_mutex_destroy函数。

pthread_mutex_t lock;

void init_mutex() {  
    pthread_mutex_init(&lock, NULL);  
}  

void cleanup_mutex() {  
    pthread_mutex_destroy(&lock);  
}  

2. 锁定和解锁操作

在访问共享资源之前,线程需要先锁定互斥锁,访问完毕后再解锁。

void access_shared_resource() {
    pthread_mutex_lock(&lock);  
    // 访问共享资源  
    pthread_mutex_unlock(&lock);  
}  

三、信号量

信号量是一种用于控制访问共享资源的同步机制,可以允许多个线程同时访问一定数量的资源。信号量的主要函数包括sem_waitsem_post

1. 信号量初始化和销毁

信号量需要先进行初始化,可以使用sem_init函数。销毁信号量则使用sem_destroy函数。

#include <semaphore.h>

sem_t sem;  

void init_semaphore() {  
    sem_init(&sem, 0, 1); // 第二个参数为0表示用于线程间同步,第三个参数为信号量初始值  
}  

void cleanup_semaphore() {  
    sem_destroy(&sem);  
}  

2. 阻塞操作

当线程需要等待信号量时,可以使用sem_wait函数。该函数会阻塞线程,直到信号量的值大于0。

void wait_for_semaphore() {
    sem_wait(&sem);  
}  

3. 唤醒操作

当条件满足时,可以使用sem_post函数增加信号量的值,从而唤醒等待的线程。

void signal_semaphore() {
    sem_post(&sem);  
}  

四、应用场景

1. 生产者-消费者问题

生产者-消费者问题是多线程编程中的经典问题,可以使用条件变量和互斥锁来解决。生产者线程生成数据,消费者线程消费数据,条件变量用于通知消费者数据已生成。

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

#define BUFFER_SIZE 10  

int buffer[BUFFER_SIZE];  
int count = 0;  
pthread_cond_t cond;  
pthread_mutex_t mutex;  

void* producer(void* arg) {  
    while (1) {  
        pthread_mutex_lock(&mutex);  
        while (count == BUFFER_SIZE) {  
            pthread_cond_wait(&cond, &mutex);  
        }  
        buffer[count++] = rand() % 100;  
        printf("Produced: %dn", buffer[count-1]);  
        pthread_cond_signal(&cond);  
        pthread_mutex_unlock(&mutex);  
    }  
    return NULL;  
}  

void* consumer(void* arg) {  
    while (1) {  
        pthread_mutex_lock(&mutex);  
        while (count == 0) {  
            pthread_cond_wait(&cond, &mutex);  
        }  
        int data = buffer[--count];  
        printf("Consumed: %dn", data);  
        pthread_cond_signal(&cond);  
        pthread_mutex_unlock(&mutex);  
    }  
    return NULL;  
}  

int main() {  
    pthread_t prod, cons;  
    pthread_cond_init(&cond, NULL);  
    pthread_mutex_init(&mutex, NULL);  
    pthread_create(&prod, NULL, producer, NULL);  
    pthread_create(&cons, NULL, consumer, NULL);  
    pthread_join(prod, NULL);  
    pthread_join(cons, NULL);  
    pthread_cond_destroy(&cond);  
    pthread_mutex_destroy(&mutex);  
    return 0;  
}  

2. 哲学家就餐问题

哲学家就餐问题可以使用信号量来解决。每个哲学家需要两个叉子才能进餐,可以将每个叉子视为一个信号量。

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

#define NUM_PHILOSOPHERS 5  

sem_t forks[NUM_PHILOSOPHERS];  

void* philosopher(void* arg) {  
    int id = (int)(size_t)arg;  
    int left = id;  
    int right = (id + 1) % NUM_PHILOSOPHERS;  
    while (1) {  
        printf("Philosopher %d is thinking.n", id);  
        sem_wait(&forks[left]);  
        sem_wait(&forks[right]);  
        printf("Philosopher %d is eating.n", id);  
        sleep(1);  
        sem_post(&forks[right]);  
        sem_post(&forks[left]);  
    }  
    return NULL;  
}  

int main() {  
    pthread_t philosophers[NUM_PHILOSOPHERS];  
    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {  
        sem_init(&forks[i], 0, 1);  
    }  
    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {  
        pthread_create(&philosophers[i], NULL, philosopher, (void*)(size_t)i);  
    }  
    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {  
        pthread_join(philosophers[i], NULL);  
    }  
    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {  
        sem_destroy(&forks[i]);  
    }  
    return 0;  
}  

以上是C语言中实现唤醒和阻塞操作的详细方法和应用场景。通过合理使用条件变量、互斥锁和信号量,可以有效地解决多线程编程中的同步问题,提高程序的并发性能和稳定性。在实际应用中,可以根据具体需求选择合适的同步机制。

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