Linux | 什么是条件变量

条件变量(为了实现多个线程间的同步操作)

  与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用
  条件变量使我们可以阻塞等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步 的一种机制,主要包括两个动作:

  • 一个线程等待"条件变量的条件成立"而挂起;
  • 另一个线程使 “条件成立”(给出条件成立信号)。

【原理】

  条件的检测是在互斥锁的保护下进行的。线程在改变条件状态之前必须首先锁住互斥量。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量 可以被用来实现这两进程间的线程同步。

【条件变量的操作流程如下】

1. 初始化

2. 等待条件成立(这一步是要在加锁之后进行的)

3. 激活条件变量

4. 清除条件变量

#include <pthread.h>

// 初始化条件变量
int pthread_cond_init(pthread_cond_t *cond,
						pthread_condattr_t *cond_attr);
//或者
pthread_cond_tcond=PTHREAD_COND_INITIALIER

// 阻塞等待,并自动对mutex进行解锁,使得其它线程可以获得加锁的权利,当pthread_cond_wait返回的时候又自动给mutex加锁。
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);

// 限时等待
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,
						const timespec *abstime);
/*timewait()可以设置等待时间,如果条件仍未成立,返回ETIMEOUT(加锁保证只有一个线程wait);*/

// 销毁条件变量cond,并解除所有线程的阻塞
int pthread_cond_destroy(pthread_cond_t *cond);

// 至少唤醒一个等待该条件的线程
int pthread_cond_signal(pthread_cond_t *cond);

// 唤醒等待该条件的所有线程
int pthread_cond_broadcast(pthread_cond_t *cond);  

示例代码:

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

int count = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//互斥锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//条件变量

void * add(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&mutex);
        count++;
        if(count == 5)
        {
            pthread_cond_signal(&cond);
        }
        sleep(1);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}

void * printf_1(void *arg)
{
    while(1)
    {
        while(count != 5)
        {
            pthread_cond_wait(&cond,&mutex);
        }
        printf("count = %d\n",count);
        count = 0;
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}
int main()
{
    pthread_t id1;
    pthread_t id2;

    pthread_mutex_init(&mutex,NULL);
    pthread_cond_init(&cond,NULL);

    pthread_create(&id1,NULL,add,NULL);
    pthread_create(&id2,NULL,printf_1,NULL);

    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
}

 

虚假唤醒(spurious wakeup)

虚假唤醒(spurious wakeup)在采用条件等待时,我们使用的是:

while(条件不满足)
{  
   condition_wait(cond, mutex);  
}  
// 而不是:  
If( 条件不满足 )
{  
   Condition_wait(cond,mutex);  
}   

这是因为可能会存在虚假唤醒”spurious wakeup”的情况。

  也就是说,即使没有线程调用condition_signal, 原先调用condition_wait的函数也可能会返回。此时线程被唤醒了,但是条件并不满足,这个时候如果不对条件进行检查而往下执行,就可能会导致后续的处理出现错误。

  虚假唤醒在linux的多处理器系统中/在程序接收到信号时可能会发生。在Windows系统和JAVA虚拟机上也存在。在系统设计时应该可以避免虚假唤醒,但是这会影响条件变量的执行效率,而既然通过while循环就能避免虚假唤醒造成的错误,因此程序的逻辑就变成了while循环的情况。

补充:C++11下实现

#include<iostream>
#include<condition_variable>
#include<mutex>
#include<thread>

using namespace std;

std::condition_variable cv;//条件变量
std::mutex mtx;//互斥锁
int isRead = 1;//可以理解为 通过此参数来决定当前应该执行哪个线程
const int n = 10;

void print_1()
{
	std::unique_lock<std::mutex> lc(mtx);
	int i = 0;
	while (i < n)
	{
		while (isRead != 1)
		{
			cv.wait(lc);
			//cout<<"print_1: "<<isRead<<endl;
		}
		cout << "A";
		isRead = 2;
		++i;
		cv.notify_all();
	}
}

void print_2()
{
	std::unique_lock<std::mutex> lc(mtx);
	int i = 0;
	while (i < n)
	{
		while (isRead != 2)
		{
			cv.wait(lc);
			//cout<<"print_2: "<<isRead<<endl;
		}
		cout << "B";
		isRead = 3;
		++i;
		cv.notify_all();
	}
}

void print_3()
{
	std::unique_lock<std::mutex> lc(mtx);
	int i = 0;
	while (i < n)
	{
		while (isRead != 3)
		{
			cv.wait(lc);
			//cout<<"print_3: "<<isRead<<endl;
		}
		cout << "C ";
		isRead = 1;
		++i;
		cv.notify_all();
	}
}

int main()
{
	std::thread t1(print_1);
	std::thread t2(print_2);
	std::thread t3(print_3);

	t1.join();
	t2.join();
	t3.join();

	return 0;
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值