线程同步 | 条件变量

本文探讨了线程同步中的饥饿问题,介绍了条件变量作为同步机制如何确保线程按序访问临界资源。通过实例演示了条件变量的初始化、使用(包括等待和唤醒操作)以及在多线程环境中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.提出问题

在纯互斥环境下,线程会产生饥饿问题;每个线程如果申请不到锁,就会进入阻塞,当特定情况下被唤醒(全部唤醒),线程之间是没有一定的顺序的。

**如果在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,这就是同步!**它能解决线程饥饿问题,能按照一定的顺序让线程完成工作。

2.条件变量

同步是在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源;条件变量是实现同步的一种机制,既然要保证数据安全那么就要有互斥锁,当线程首先访问到对应的锁资源,如果申请不到,再去对应的条件变量的队列中排队,所以条件变量必须依赖锁的使用

当一个线程退出临界区,对应的条件条件变量就通知队列中的线程,唤醒一个或者一批!

在这里插入图片描述

张三(线程)进入自习室(临界区)自习,并反锁门,其他人(其他线程)也去自习先自习室门口找钥匙(先申请锁),发现有人,就去排队。当张三离开自习室,将钥匙放在门口(释放锁),并敲一下铃铛让其他的人自习。

所以至少看了来条件变量有两个东西,1.按照一定顺序排队的数据结构,2.有对用的通信机制,唤醒队列等待的线程。

条件变量是一个概念(任何计算机中的概念),落实到对应的代码实现上先描述再组织。

3.条件变量接口与使用

初始化条件变量

  • 方法一:静态分配
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • 方法二:动态分配
int pthread_cond_init(pthread_cond_t *restrict cond,
                      const pthread_condattr_t *restrictattr);
参数:
	cond:要初始化的条件变量
	attr:NULL

销毁

int pthread_cond_destroy(pthread_cond_t *cond)

注意:静态分配的条件变量不用自己手动销毁

等待条件满足

int pthread_cond_wait(pthread_cond_t *restrict cond,
					  pthread_mutex_t *restrict mutex);
参数:
	cond:要在这个条件变量上等待
	mutex:互斥量

唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond)

快速实现

#include <iostream>
#include <pthread.h>
#include <unistd.h>

using namespace std;
int g_val = 0;

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void* count1(void * args)
{
    pthread_detach(pthread_self());

    uint64_t num = *(uint64_t*)args;

    while(true)
    {
        // 这个地方不对临界资源加锁保护是不行的!
        cout << "pthread num is :  " << num << " : the add of g_val number is : " << g_val++ << endl;
        // 休眠一下,不然会有饥饿问题
        sleep(1); 
    }
}

void test1()
{
    // 1.多线程对一个全局的变量修改(这是有问题的例子)
    // 这里是64位系统,避免(void*)i警告用uint64_t
    for(uint64_t i = 0;i < 5; i++)
    {
        pthread_t pid;
        // 这里i传入地址,是由问题的,如果不是堆上的空间不要传地址,i传地址改变也是共享资源会有并发问题
        pthread_create(&pid,nullptr,count1,(void*)&i);   
    }
    while(true){}

}

void* count2(void* args)
{
    uint64_t num = (uint64_t)args;

    while(true)
    {
        pthread_mutex_lock(&lock);
        /*
            这个地方:满足条件就去条件变量下等待!所以说让一个线程去休眠,是临界资源(临界资源也是有状态的)不就绪。
            怎么知道临界资源是就绪还是不就绪的?判断出来的!判断是访问临界资源吗?必须是的,也就是判断必须在加锁之后!!!
            pthread_cond_wait让线程等待的时候,会自动释放锁!
            其他线程来的时候再去竞争锁,pthread_mutex_lock(&lock);锁竞争完之后pthread_cond_wait(&cond,&lock);这里添加变量的条件总是满足的
            一上来就去条件变量下排队,等待唤醒(pthread_cond_signal(默认唤醒第一个)或pthread_cond_broadcast(唤醒一批))
        */
        pthread_cond_wait(&cond,&lock);
        cout << "pthread num is :  " << num << " : the add of g_val number is : " << g_val++ << endl;
        pthread_mutex_unlock(&lock);
    }
}

void test2()
{
    for(uint64_t i = 0;i < 5;i++)
    {
        pthread_t pid;
        pthread_create(&pid,nullptr,count2,(void*)i);
    }


    cout << "create thread success !" << endl;
    sleep(3);

    while(true)
    {
        sleep(1);
        // 唤醒:pthread_cond_signal(默认唤醒第一个)或pthread_cond_broadcast(唤醒一批)
        // pthread_cond_signal(&cond);
        pthread_cond_broadcast(&cond);
        cout << "signal thread~" << endl;
    }
}

int main()
{
    test2();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值