Linux系统中线程同步方式中的条件变量操作方法

简介: 大家好,今天主要和大家聊一聊,如何使用Linux中线程同步方式中的条件变量。

0ddf72e916ee42988a125590cedca7a9.png

第一:条件变量基本简介

   条件变量是线程可用的另一种同步机制,条件变量用于自动阻塞线程,知道某个特定事件或某个条件满足为止,通常情况下,条件变量是和互斥锁一起搭配使用的。使用条件变量主要包括两个动作:

1、一个线程等待某个条件满足而被阻塞。


2、另一个线程中,条件满足时发出“信号”。


   例子:为了说明这个问题,来看一个没有使用条件变量的例子,生产者---消费者模式,生产者这边负责生产产 品、而消费者负责消费产品,对于消费者来说,没有产品的时候只能等待产品出来,有产品就使用它。

这里我们使用一个变量来表示这个产品,生产者生产一件产品变量加1,消费者消费一次变量减1.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
static pthread_mutex_t mutex;
static int g_avail = 0;
/* 消费者线程 */
static void *consumer_thread(void *arg)
{
 for ( ; ; ) {
 pthread_mutex_lock(&mutex);//上锁
 while (g_avail > 0)
 g_avail--; //消费
 pthread_mutex_unlock(&mutex);//解锁
 }
 return (void *)0;
}
/* 主线程(生产者) */
int main(int argc, char *argv[])
{
 pthread_t tid;
 int ret;
 /* 初始化互斥锁 */
 pthread_mutex_init(&mutex, NULL);
 /* 创建新线程 */
 ret = pthread_create(&tid, NULL, consumer_thread, NULL);
 if (ret) {
 fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
 exit(-1);
 }
 for(;;){
   pthread_mutex_lock(&mutex);  //上锁
   g_avail++;  //生产
   pthread_mutex_unlock(&mutex); //解锁
 }
 exit(0);
 }   

分析:上述代码虽然可行,但由于新线程中会不停的循环检查全局变量 g_avail 是否大于 0,故而造成 CPU 资 源的浪费。采用条件变量这一问题就可以迎刃而解。

第二:条件变量初始化方法

 条件变量使用pthread_cond_t数据类型来表示,在使用条件变量之前必须对其进行初始化。

   pthread_cond_init()函数原型如下所示:

#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

同样,使用这些函数需要包含头文件,使用pthread_cond_init()函数初始化条件变量,当不再使用时,使用pthread_cond_destroy()销毁条件变量。


     参数cond指向pthread_cond_t条件变量对象,对于pthread_cond_init()函数,类似于互斥锁,在初始化 条件变量时设置条件变量的属性,参数 attr 指向一个 pthread_condattr_t 类型对象,pthread_condattr_t 数据类 型用于描述条件变量的属性。可将参数 attr 设置为 NULL,表示使用属性的默认值来初始化条件变量,与使 用 PTHREAD_COND_INITIALIZER 宏相同。


     函数调用成功返回0,失败将返回一个非0值的错误码。


使用注意:


1、在使用条件变量之前必须对条件变量进行初始化操作,使用 PTHREAD_COND_INITIALIZER 宏或 者函数 pthread_cond_init()都行;


2、对已经初始化的条件变量再次进行初始化,将可能会导致未定义行为;


3、对没有进行初始化的条件变量进行销毁,也将可能导致未定义行为;


4、对某个条件变量而言,仅当没有任何线程等待它时,将其销毁才是最安全

第三:通知和等待条件变量

 条件变量的主要操作便是发送信号(signal)和等待。发送信号操作即是通知一个或多个处于等待状态的线程,某个共享变量的状态已经改变,这些处于等待状态的线程收到通知之后便会被唤醒,唤醒之后再检 查条件是否满足。等待操作是指在收到一个通知前一直处于阻塞状态。


    函数pthread_cond_signal()和pthread_cond_broadcast()均可向指定的条件变量发送信号,通知一个或多个处于等待状态的线程。调用pthread_cond_wait()函数是线程阻塞,直到收到条件变量的通知。


    pthread_cond_signal()和 pthread_cond_broadcast()函数原型如下所示:

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

当程序当中使用条件变量,当判断某个条件不满足时,调用 pthread_cond_wait()函数将线程设置为等待 状态(阻塞)。pthread_cond_wait()函数包含两个参数:

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

ond:指向需要等待的条件变量,目标条件变量;


   mutex:参数 mutex 是一个 pthread_mutex_t 类型指针,指向一个互斥锁对象;前面开头便给大家介绍 了,条件变量通常是和互斥锁一起使用,因为条件的检测(条件检测通常是需要访问共享资源的)是在互斥 锁的保护下进行的,也就是说条件本身是由互斥锁保护的。


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


在 pthread_cond_wait()函数内部会对参数 mutex 所指定的互斥锁进行操作,通常情况下,条件判断以及 pthread_cond_wait()函数调用均在互斥锁的保护下,也就是说,在此之前线程已经对互斥锁加锁了。调用 pthread_cond_wait()函数时,调用者把互斥锁传递给函数,函数会自动把调用线程放到等待条件的线程列表 上,然后将互斥锁解锁;当 pthread_cond_wait()被唤醒返回时,会再次锁住互斥锁。


代码实例:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
static pthread_mutex_t mutex; //定义互斥锁
static pthread_cond_t cond; //定义条件变量
static int g_avail = 0; //全局共享资源
/* 消费者线程 */
static void *consumer_thread(void *arg)
{
 for ( ; ; ) {
 pthread_mutex_lock(&mutex);//上锁
 while (0 >= g_avail)
 pthread_cond_wait(&cond, &mutex);//等待条件满足
 while (0 < g_avail)
 g_avail--; //消费
 pthread_mutex_unlock(&mutex);//解锁
 }
 return (void *)0;
}
/* 主线程(生产者) */
int main(int argc, char *argv[])
{
 pthread_t tid;
 int ret;
 /* 初始化互斥锁和条件变量 */
 pthread_mutex_init(&mutex, NULL);
 pthread_cond_init(&cond, NULL);
 /* 创建新线程 */
 ret = pthread_create(&tid, NULL, consumer_thread, NULL);
 if (ret) {
 fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
 exit(-1);
 }
 for ( ; ; ) {
 pthread_mutex_lock(&mutex);//上锁
 g_avail++; //生产
 pthread_mutex_unlock(&mutex);//解锁
 pthread_cond_signal(&cond);//向条件变量发送信号
 }
 exit(0);
}

 全局变量 g_avail 作为主线程和新线程之间的共享资源,两个线程在访问它们之间首先会对互斥锁进行 上锁,消费者线程中,当判断没有产品可被消费时(g_avail <= 0),调用 pthread_cond_wait()使得线程陷入 等待状态,等待条件变量,等待生产者制造产品;调用 pthread_cond_wait()后线程阻塞并解锁互斥锁;而在 生产者线程中,它的任务是生产产品(使用g_avail++来模拟),产品生产完成之后,调用pthread_mutex_unlock() 将互斥锁解锁,并调用 pthread_cond_signal()向条件变量发送信号;这将会唤醒处于等待该条件变量的消费 者线程,唤醒之后再次自动获取互斥锁,然后再对产品进行消费(g_avai--模拟)。


总结:Linux系统中为了方便线程对共享资源的访问,条件变量在其中也会发挥重要作用。

目录
相关文章
|
4天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
19 3
|
4天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
16 2
|
4天前
|
安全 网络协议 Linux
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。通过掌握 ping 命令,读者可以轻松测试网络连通性、诊断网络问题并提升网络管理能力。
18 3
|
7天前
|
安全 Linux 数据安全/隐私保护
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。本文介绍了使用 `ls -l` 和 `stat` 命令查找文件所有者的基本方法,以及通过文件路径、通配符和结合其他命令的高级技巧。还提供了实际案例分析和注意事项,帮助读者更好地掌握这一操作。
23 6
|
11天前
|
缓存 监控 Linux
|
15天前
|
Linux Shell 数据安全/隐私保护
|
15天前
|
域名解析 网络协议 安全
|
21天前
|
运维 监控 网络协议
|
22天前
|
监控 Linux Shell
|
7天前
|
Linux
在 Linux 系统中,`find` 命令是一个强大的文件查找工具
在 Linux 系统中,`find` 命令是一个强大的文件查找工具。本文详细介绍了 `find` 命令的基本语法、常用选项和具体应用示例,帮助用户快速掌握如何根据文件名、类型、大小、修改时间等条件查找文件,并展示了如何结合逻辑运算符、正则表达式和排除特定目录等高级用法。
31 6