linux多线程的同步手段 — 条件变量

在多线程开发中,有时候有些线程可能只需要在某种条件成立时才去执行,并不想要这个线程一直被调度,也不想要一直去查询某个条件是否成立了。

基于这样的一种想法,有没有这样的一种方式,在某个条件没有成立的时候,这个线程平时就被阻塞挂起,等到这个条件成立了,通知一下这个线程条件已经成立了,可以去执行相关的操作了。话说到这个份上了,答案肯定是有的,那就是线程同步的其中一种手段 — 条件变量。

条件变量用于自动阻塞线程,直到某个条件满足或者特定的事件发生为止。

本文不多条件变量做很多的深入分析,只分享一下条件变量的一些api函数以及简单的用法示例。

1、条件变量的定义和初始化

 条件变量的定义使用 pthread_cond_t 这个数据类型来定义,如下:

pthread_cond_t MyCond;

对条件变量的初始化可以采用两种方式:

(1)使用宏 PTHREAD_COND_INITIALIZER

pthread_cond_t MyCond = PTHREAD_COND_INITIALIZER;

(2)使用函数 pthread_cond_init()

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, 
                      const pthread_condattr_t *attr);
1)参数 cond 指向 pthread_cond_t 条件变量对象;
2)参数 attr 指向一个 pthread_condattr_t 类型对象,
   pthread_condattr_t 数据类型用于描述条件变量的属性。
   这个条件变量的属性内容有点多,感兴趣的可以查资料学习!

2、销毁条件变量

如果条件变量使用完之后,不打算再使用了,需要销毁掉这个条件变量,如下:

#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);

3、等待条件变量

条件变量定义好了之后,在你想要等待某个条件的地方就可以使用条件变量等待的方式进行阻塞,一直等到条件成立之后再去执行。

等待条件变量的api函数如下:

#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

参数:
1)cond :指向需要等待的条件变量,目标条件变量;
2)mutex :参数 mutex 是一个 pthread_mutex_t 类型指针,指向一个互斥锁对象;

返回值:调用成功返回 0;失败将返回一个非 0 值的错误码。

4、通知条件变量

当我们在程序中使用等待条件变量成立的api之后,线程已经处于阻塞了,一直在等待着条件变量的成立,那这个阻塞等待的条件变量怎么知道它有没有成立呢?

这个时候就需要有一种通知方式,在某个条件变量成立的时候,告诉因为这个条件变量而阻塞的线程,我已经成立,你可以执行你相应的操作了。

这个过程用到的api如下:

#include <pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cond);

int pthread_cond_signal(pthread_cond_t *cond);

这两个api都可以用于通知条件变量成立,但是这两个api有什么不同呢?

首先,函数 pthread_cond_signal()和 pthread_cond_broadcast()均可向指定的条件变量发送信号,通知处于等待状态的线程。

不同之处在于,pthread_cond_signal()函数至少能唤醒一个线程,而 pthread_cond_broadcast()函数则能唤醒所有线程。

使用 pthread_cond_broadcast()函数总能产生正确的结果,唤醒所有等待状态的线程。但函数 pthread_cond_signal()会更为高效,因为它只需确保至少唤醒一个线程即可,所以如果我们的程序当中,只有一个处于等待状态的线程,建议使用 pthread_cond_signal()。

5、举个例子说明一下

在这里,我创建两个线程,pthread_Task1 和 pthread_Task2。pthread_Task2 用条件变量去阻塞等待,pthread_Task1 里面计数,每计数四次发送一个条件变量成立的通知,让pthread_Task2 执行一次,如此循环往复。示例代码如下:​​​​

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

pthread_mutex_t Mutex1,Mutex2;
pthread_cond_t cond;

void *pthread_Task1(void *arg)
{
  int cnt = 0;

	while(1)
	{
		pthread_mutex_lock(&Mutex1);
		printf("Task1...\r\n");
		if(cnt++ % 4 == 0)
			//pthread_cond_signal(&cond);
			pthread_cond_broadcast(&cond);
		pthread_mutex_unlock(&Mutex1);
		sleep(1);
	}
}

void *pthread_Task2(void *arg)
{
	while(1)
	{
		pthread_cond_wait(&cond,&Mutex2);
		printf("Task2...\r\n");
	}
}								


int main(int argv,char **argc)
{
	pthread_t pthread_id1,pthread_id2;

	pthread_mutex_init(&Mutex1,NULL);
	pthread_mutex_init(&Mutex2,NULL);

	pthread_cond_init(&cond,NULL);

	pthread_create(&pthread_id1,NULL,pthread_Task1,NULL);
	pthread_create(&pthread_id2,NULL,pthread_Task2,NULL);

	pthread_join(pthread_id1,NULL);
	pthread_join(pthread_id2,NULL);

	exit(0);

	printf("main finish!!!\r\n");

}

编译运行之后的结果如下:

从运行结果的截图可以看出,Task1每执行4次会发送一次条件变量成立的通知,Task2会执行一次,如此循环往复的执行。这个结果和代码里面的设计逻辑是相吻合的。感兴趣的朋友可以运行验证一下。

猜你喜欢

转载自blog.csdn.net/weixin_43866583/article/details/129456319