线程同步与条件变量

1 为什么需要线程同步?

当多个线程同时访问共享资源时,可能会出现:

  • 竞态条件:执行结果依赖于线程执行的顺序
  • 数据不一致:线程读取到中间状态的数据
  • 死锁:线程相互等待对方释放资源

2 同步概念与竞态条件

• 同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步

• 竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。在线程场景下,这种问题也不难理解

3 条件变量

• 当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。

• 例如一个线程访问队列时,发现队列为空,它只能等待,等到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。

基本概念:

条件变量是多线程编程中的同步机制,用于线程间的通信和协调。它允许一个或多个线程等待某个条件成立,而其他线程在条件满足时通知等待的线程。

核心功能

1. 等待条件

  • 线程检查条件,如果条件不满足,则进入等待状态
  • 等待时会自动释放互斥锁,让其他线程能修改共享数据

2. 通知等待线程

  • signal():唤醒一个等待线程
  • broadcast():唤醒所有等待线程

3 条件变量函数

初始化

条件变量的初始化和互斥锁几乎一样

• 方法1,静态分配:

cpp 复制代码
// 静态初始化(全局或静态变量)
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// 不需要调用 pthread_cond_init
// 也不需要调用  pthread_cond_destroy

方法2,动态分配: pthread_cond_init 函数

pthread_cond_init是 POSIX 线程库中用于初始化条件变量的函数。条件变量用于线程间的同步,允许线程等待某个条件成立。

cpp 复制代码
#include <pthread.h>

int pthread_cond_init(pthread_cond_t *restrict cond,
                     const pthread_condattr_t *restrict attr);

参数说明

cond

  • 指向要初始化的条件变量的指针

attr

  • 指向条件变量属性的指针
  • 如果为 NULL,则使用默认属性

返回值

  • 成功:返回 0
  • 失败:返回错误码(非零值)

销毁

cpp 复制代码
int pthread_cond_destroy(pthread_1 cond_t *cond)

等待

pthread_cond_wait是 POSIX 线程(pthread)库中用于条件变量等待的函数。

cpp 复制代码
#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *restrict cond,
                      pthread_mutex_t *restrict mutex);

参数说明

参数 说明
cond 指向条件变量的指针
mutex 指向互斥锁的指针

工作原理

1. 原子操作三部曲

当线程调用 pthread_cond_wait 时,它会原子地执行以下操作:

cpp 复制代码
// 伪代码表示其行为:
pthread_mutex_unlock(mutex);    // 1. 释放互斥锁
wait_for_signal(cond);          // 2. 等待条件变量
pthread_mutex_lock(mutex);      // 3. 重新获取互斥锁

2. 为什么要用互斥锁配合?

条件变量必须与互斥锁一起使用,原因:

  • 防止竞争条件(race condition)
  • 确保检查条件和等待是原子操作
  • 避免信号丢失

唤醒等待

cpp 复制代码
int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒在该条件变量下等待的全部线程
//唤醒在该条件变量下等待的一个线程
int pthread_cond_signal(pthread_cond_t *cond);

代码实践:简单看一下函数应用

cpp 复制代码
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *active(void *arg)
{
    std::string name = static_cast<const char *>(arg);
    while (true)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond, &mutex);
        std::cout << name << " 活动..." << std::endl;
        pthread_mutex_unlock(&mutex);
    }
}
int main(void)
{
    pthread_t t1, t2;
    pthread_create(&t1, NULL, active, (void *)"thread-1");
    pthread_create(&t2, NULL, active, (void *)"thread-2");
    sleep(3); // 可有可无,这里确保两个线程已经在运行
    while (true)
    {
        // 对比测试
        //pthread_cond_signal(&cond); // 唤醒一个线程
        pthread_cond_broadcast(&cond); // 唤醒所有线程
        sleep(1);
    }
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
}

当唤醒一个线程时一个线程一个线程逐个出现,当唤醒所有线程时两个两个的一起出现

相关推荐
sin_hielo2 小时前
leetcode 2483
数据结构·算法·leetcode
dishugj2 小时前
【linux】Redhat 6.3系统安装zabbix-agent软件包,无法使用YUM源问题
linux·运维·zabbix
无奈笑天下3 小时前
【麒麟镜像vmtools异常排查指导书】
linux·运维·经验分享·云计算·kylin
Xの哲學3 小时前
Linux多级时间轮:高精度定时器的艺术与科学
linux·服务器·网络·算法·边缘计算
大头流矢3 小时前
归并排序与计数排序详解
数据结构·算法·排序算法
阿闽ooo4 小时前
外观模式:从家庭电源控制看“简化接口“的设计智慧
c++·设计模式·外观模式
一路往蓝-Anbo4 小时前
【第20期】延时的艺术:HAL_Delay vs vTaskDelay
c语言·数据结构·stm32·单片机·嵌入式硬件
QT 小鲜肉4 小时前
【Linux命令大全】001.文件管理之mmove命令(实操篇)
linux·服务器·前端·chrome·笔记
Winner13004 小时前
查看rk3566摄像头设备、能力、支持格式
linux·网络·人工智能
QT 小鲜肉4 小时前
【Linux命令大全】001.文件管理之mdel命令(实操篇)
linux·运维·服务器·chrome·笔记