线程间竞争与同步机制详解

在多线程编程中,线程间竞争与同步是至关重要的概念。正确使用同步机制可以有效避免数据不一致、脏数据等问题。本文将详细介绍线程间竞争的基本概念、互斥量、信号量、死锁以及条件变量,并讲解主要使用的函数,附带代码示例和注释。

一、基本概念

  1. 原子操作:中途不会被打断的操作称为原子操作,确保不会被其他线程影响。
  2. 竞争与同步:同一进程中的线程共享大多数资源,竞争可能导致数据损坏或不一致。通过同步机制,线程可以相互协调,避免出现这种情况。
  3. 临界区与临界资源
    • 临界区:多个线程同时访问的代码块。
    • 临界资源:被多个线程同时访问的数据。

二、互斥量(互斥锁)

互斥量是保护临界区的常用同步机制。以下是使用 pthread 库的互斥量相关函数及其讲解:

  1. pthread_mutex_t:互斥量的数据类型,用于定义互斥量变量。

  2. pthread_mutex_init

    • 原型int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
    • 功能:初始化一个互斥量。通常将 attr 设为 NULL
  3. pthread_mutex_lock

    • 原型int pthread_mutex_lock(pthread_mutex_t *mutex);
    • 功能:对互斥量进行加锁。成功加锁后继续执行,失败则阻塞,直到互斥量解锁。
  4. pthread_mutex_trylock

    • 原型int pthread_mutex_trylock(pthread_mutex_t *mutex);
    • 功能:尝试对互斥量加锁。成功返回 0,失败则立即返回 EBUSY
  5. pthread_mutex_unlock

    • 原型int pthread_mutex_unlock(pthread_mutex_t *mutex);
    • 功能:解锁互斥量。
  6. pthread_mutex_destroy

    • 原型int pthread_mutex_destroy(pthread_mutex_t *mutex);
    • 功能:销毁互斥量,释放相关资源。

示例代码

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

pthread_mutex_t mutex;  // 定义互斥量

void* thread_function(void* arg) {
    pthread_mutex_lock(&mutex);  // 加锁
    // 临界区代码
    printf("Thread %ld is in the critical section.\n", (long)arg);
    pthread_mutex_unlock(&mutex);  // 解锁
    return NULL;
}

int main() {
    pthread_t threads[5];
    pthread_mutex_init(&mutex, NULL);  // 初始化互斥量

    for (long i = 0; i < 5; i++) {
        pthread_create(&threads[i], NULL, thread_function, (void*)i);
    }

    for (int i = 0; i < 5; i++) {
        pthread_join(threads[i], NULL);  // 等待线程结束
    }

    pthread_mutex_destroy(&mutex);  // 销毁互斥量
    return 0;
}

三、信号量

信号量用于控制访问有限资源的线程数量。下面是信号量的使用示例及主要函数讲解:

  1. sem_t:信号量的数据类型。

  2. sem_init

    • 原型int sem_init(sem_t *sem, int pshared, unsigned int value);
    • 功能:初始化信号量。pshared 为 0 时表示信号量只能在本进程内使用,value 是信号量的初始值。
  3. sem_wait

    • 原型int sem_wait(sem_t *sem);
    • 功能:对信号量进行减1操作。如果信号量为0则阻塞,直到信号量大于0。
  4. sem_trywait

    • 原型int sem_trywait(sem_t *sem);
    • 功能:尝试对信号量减1操作。成功返回 0,失败返回 EAGAIN
  5. sem_post

    • 原型int sem_post(sem_t *sem);
    • 功能:对信号量进行加1操作。
  6. sem_destroy

    • 原型int sem_destroy(sem_t *sem);
    • 功能:销毁信号量,释放资源。

示例代码

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

sem_t semaphore;  // 定义信号量

void* thread_function(void* arg) {
    sem_wait(&semaphore);  // P操作,减1
    // 临界区代码
    printf("Thread %ld is in the critical section.\n", (long)arg);
    sem_post(&semaphore);  // V操作,加1
    return NULL;
}

int main() {
    pthread_t threads[5];
    sem_init(&semaphore, 0, 2);  // 初始化信号量,初始值为2

    for (long i = 0; i < 5; i++) {
        pthread_create(&threads[i], NULL, thread_function, (void*)i);
    }

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

    sem_destroy(&semaphore);  // 销毁信号量
    return 0;
}

四、死锁

1. 什么是死锁

死锁是指多个进程或线程互相等待对方释放资源,导致所有进程或线程都无法继续执行。

2. 死锁的四大必要条件
  • 资源互斥:资源只能被一个进程或线程占用。
  • 占有且请求:已占有的资源可继续请求新的资源。
  • 资源不可剥夺:已占有的资源不能被强制获取。
  • 环路等待:形成等待环路。
3. 如何防止死锁
  • 破坏资源互斥:允许资源共享(但难以实现)。
  • 破坏占有且请求:采用预分配资源的方式。
  • 破坏资源不可剥夺:允许进程释放资源后再请求。
  • 破坏等待环路:按顺序请求资源。

五、条件变量

条件变量用于线程间的协作,可以让线程在特定条件下进入睡眠或唤醒。以下是条件变量的主要函数及讲解:

  1. pthread_cond_t:条件变量的数据类型。

  2. pthread_cond_init

    • 原型int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
    • 功能:初始化条件变量。通常将 attr 设为 NULL
  3. pthread_cond_wait

    • 原型int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
    • 功能:让当前线程进入睡眠状态并释放互斥锁,直到被唤醒。
  4. pthread_cond_signal

    • 原型int pthread_cond_signal(pthread_cond_t *cond);
    • 功能:唤醒一个在条件变量上等待的线程。
  5. pthread_cond_broadcast

    • 原型int pthread_cond_broadcast(pthread_cond_t *cond);
    • 功能:唤醒所有在条件变量上等待的线程。
  6. pthread_cond_destroy

    • 原型int pthread_cond_destroy(pthread_cond_t *cond);
    • 功能:销毁条件变量,释放资源。

示例代码

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

pthread_mutex_t mutex;
pthread_cond_t cond;  // 定义条件变量
int ready = 0;  // 共享变量

void* producer(void* arg) {
    pthread_mutex_lock(&mutex);
    ready = 1;  // 生产数据
    pthread_cond_signal(&cond);  // 唤醒消费者
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void* consumer(void* arg) {
    pthread_mutex_lock(&mutex);
    while (ready == 0) {  // 检查条件
        pthread_cond_wait(&cond, &mutex);  // 等待条件满足
    }
    printf("Consumer consumed the data.\n");
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t prod, cons;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    pthread_create(&cons, NULL, consumer, NULL);
    pthread_create(&prod, NULL, producer, NULL);

    pthread_join(prod, NULL);
    pthread_join(cons, NULL);

    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);
    return 0;
}

总结

线程间的竞争与同步是多线程编程的重要内容。通过理解互斥量、信号量、死锁以及条件变量的使用,以及相关函数的详细讲解,可以有效地实现线程间的协调与合作。希望本篇博客能够帮助你更好地理解这些概念并在实际编程中应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值