首页 > 系统相关 >Linux条件变量

Linux条件变量

时间:2023-11-22 09:22:40浏览次数:45  
标签:Task 变量 Linux pthread pTask tasks taskID 条件 include

1.为什么要有条件变量?

在实际应用中,常常会有如下的需求:

  • 用于反复判断一个多线程的共享条件是否满足。
//伪代码
int WaitForTrue()
{
	do{
	 pthread_mutex_lock(&m);
	
	 //验证 condition 是否为 true
	
	 //解锁,让其它线程有机会改变condition
	 pthread_mutex_unlock(&m);
	
	 //睡眠 n 秒
	  sleep(n);
	}while(condition is false);
}
return 1;

2.条件变量为什么要与互斥体对象结合使用?


pthread_mutex_lock(&m)
while(condition_is_false)
{
	pthread_mutex_unlock(&m);
	//解锁之后,等待之前,可能条件变量已经满足,信号已经发出,但是该信号可能被错过
	cond_wait(&cv);
	pthread_mutex_lock(&m);
}

如上伪代码所示,假设线程 A 在执行完第 5 行代码后 CPU 时间片被剥夺,此时另一个线程 B 获得该互斥体对象 m,然后发送条件信号,等线程 A 重新获得时间片后,由于该信号已经被错过,可能会导致线程 A 在代码第 7 行无限阻塞下去。

造成这个问题的根源是释放互斥体对象与条件变量等待唤醒不是原子操作,即解锁和等待这两个步骤必须在同一个原子操作中,才能确保 cond_wait 在唤醒之前不会有其它线程获得这个互斥体对象。

3.条件变量的使用

利用条件变量实现生产者-消费者模型

#include <pthread.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <list>
#include <iostream>

class Task
{
public:
    Task(int taskID){
        this->taskID = taskID;
    }

    void doTask(){
        std::cout << "handle a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl;
    }

private:
    int taskID;
};

pthread_mutex_t mymutex;
std::list<Task*> tasks;
pthread_cond_t mycv;

void* consumer_thread(void* param)
{
    Task* pTask = NULL;
    while(true)
    {
        pthread_mutex_lock(&mymutex);
        while(tasks.empty()){
            //如果获得了互斥锁,但是条件不合适,则pthread_cond_wait会释放锁,不往下执行
            //发生变化后,如果条件合适,则pthread_cond_wait将直接获得锁
            pthread_cond_wait(&mycv,&mymutex);
        }
        pTask = tasks.front();
        tasks.pop_front();
        pthread_mutex_unlock(&mymutex);

        if(pTask == NULL){
            continue;
        }
        pTask->doTask();
        delete pTask;
        pTask = NULL;
    }
    return NULL;

}

void* producer_thread(void* param)
{
    int taskID = 0;
    Task* pTask = NULL;

    while(true){
        pTask = new Task(taskID);
        
        pthread_mutex_lock(&mymutex);
        tasks.push_back(pTask);
        std::cout << "prodece a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl;
        pthread_mutex_unlock(&mymutex);

        //释放信号量,通知消费者线程
        pthread_cond_signal(&mycv);

        taskID++;

        //休眠1秒
        sleep(1);
    }
    return NULL;
}



int main()
{
    pthread_mutex_init(&mymutex,NULL);
    pthread_cond_init(&mycv, NULL);

    //创建5个消费者线程
    pthread_t consumerThread[5];
    for(int i = 0; i < 5; i++){
        pthread_create(&consumerThread[i],NULL,consumer_thread,NULL);
    }
    
    //创建1个生产者线程
    pthread_t producerThread;
    pthread_create(&producerThread,NULL,producer_thread,NULL);

    pthread_join(producerThread,NULL);

    for(int i = 0; i < 5; i++){
        pthread_join(consumerThread[i],NULL);
    }

    pthread_mutex_destroy(&mymutex);
    pthread_cond_destroy(&mycv);

    return 0;
}

4.条件变量的虚假唤醒

  • 即某次 pthread_cond_wait 被唤醒时, task.empty() 可能仍然为 True;
  • 因此条件变量醒来之后再次测试条件是否满足就可以解决虚假唤醒问题,因此使用 while 循环而不是 if 语句来判断条件。

c++11实现消费者-生产者模型

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


class Task
{
public:
    Task(int taskID){
        this->taskID = taskID;
    }

    void doTask(){
        std::cout << "handle a task, taskID: " << taskID << ", threadID: " << std::this_thread::get_id() << std::endl;
    }
private:
    int taskID;
};

std::mutex mymutex;
std::condition_variable mycv;
std::list<Task*> tasks;

void* consumer_thread(){
    Task* pTask = NULL;
    while(true){

        std::unique_lock<std::mutex> guard(mymutex);
        while(tasks.empty()){
            //如果获得了互斥锁,但是条件不合适,则pthread_cond_wait会释放锁,不向下执行
            //发生变化后,如果条件合适,则pthread_cond_wait将直接获得锁
            mycv.wait(guard);
        }
        pTask = tasks.front();
        tasks.pop_front();

        if(pTask == NULL){
            continue;
        }
        pTask->doTask();
        delete pTask;
        pTask = NULL;
    }
    return NULL;
}


void* producer_thread(){
    Task* pTask = NULL;
    int taskID = 0;

    while(true){
        pTask = new Task(taskID);
        //使用小括号减小guard锁的作用范围
        {
            std::lock_guard<std::mutex> guard(mymutex);
            tasks.push_back(pTask);
            std::cout << "produce a task, taskID: " << taskID << ", threadID: " << std::this_thread::get_id() << std::endl;
        }

        //释放信号量,通知消费者进程
        mycv.notify_one();

        taskID++;

        //休眠1秒
        std::this_thread::sleep_for(std::chrono::seconds(1));

    }
    return NULL;

}
int main(){
    //创建5个线程
    std::thread consumer1(consumer_thread);
    std::thread consumer2(consumer_thread);
    std::thread consumer3(consumer_thread);
    std::thread consumer4(consumer_thread);
    std::thread consumer5(consumer_thread);


    //创建1个生产者线程
    std::thread producer(producer_thread);

    producer.join();
    consumer1.join();
    consumer2.join();
    consumer3.join();
    consumer4.join();
    consumer5.join();

    return 0;
}

标签:Task,变量,Linux,pthread,pTask,tasks,taskID,条件,include
From: https://www.cnblogs.com/ljx-0122/p/17846921.html

相关文章

  • 三、Linux基本使用和常用命令
    Linux基本使用和常用命令1、登录Linux操作系统方式1.图形化界面基于xwindowSystem显示框架开发由KDE(类似于苹果系统)、GNOME.v.3.0提供图形化桌面环境2.虚拟控制台文本方式(Ctrl+Alt+F3) 3.Web网页登录前提是需要打开网页控制台,由cock.pit程序提供‘’‘......
  • shell 编程条件语句
    shelltest  测试0为真test-a/etc/fstabecho$?test-e/etc/fstabecho$? -a,-e#测试文件是否存在-a有bug#取反会有变化test+选项对象参数test-f#只看文件-r#是否有读的权限-w#是否有写的权限-x#是否有执行的权限-d#目录-f#文件[-e/etc/fs......
  • Linux平台下的进程控制
    进程创建关于进程的创建,在Linux进程状态与进程优先级部分已进行过讨论,为了保证文章的完整性,这里再进行简述。在linux平台下,创建进程有两种方式:运行指令和使用系统调用接口,前者是在指令层面创建进程,后者是在代码层面创建进程。在C/C++代码中,使用fork(2)创建子进程,fork(2)的工作有3......
  • 第12周linux课堂总结
        这周的linux课程我们学习了存储管理,从连接方式上,存储分为以下3种类型,分别是本地存储、外部存储和网络存储,从工作原理上,硬盘分为固态硬盘和机械硬盘,从硬盘接口上,硬盘分为以下几种类型,IDE——SATA硬盘,SCSI——SAS硬盘,其他——PCIe、FC硬盘,SAS是新一代的SCSI技术,SAS硬盘......
  • Linux中的$符号的三种常见用法
    本文总结了Linux中的$符号的各种用法用法一:显示脚本参数($0、$?、$*、$@、$#、$$、$!)(本质上属于变量替换)$0:就是该bash文件名,个位数的,可直接使用数字,但两位数以上,则必须使用{}符号来括住,如${10}.$?:是上一指令的返回值,成功是0,不成功是1。一般来说,UNIX(linux)系统的进程以执行......
  • CentOS7环境下Linux命令的基本指令(二)
    权限管理命令权限管理命令:chmod命令名称:chmod命令英文原意:changethepermissionsmodeofafile命令所在路径:/bin/chmod执行权限:所有用户语法:chmod[{ugoa}{±=}{rwx}][文件或目录]chmod[mode=421][文件或目录]-R递归修改功能描述:改变文件或目录的权限指令解析:可以对不同......
  • linux安装tomcat
    1.Centos+Tomcat在线安装sudoyuminstalljava-1.7.0-openjdk-devel#安装javayuminstalltomcat#在线安装tomcatsystemctlstarttomcat#启动tomcat服务器yuminstalltomcat-webappscd/usr/share/tomcat/webapps#DeleteunnecessarythingsmkdirROOT......
  • 08-基础SQL-DQL(数据查询语言)-条件查询(WHERE)
    DQL-介绍(常用)DQL英文全称是DataQueryLanguage(数据查询语言),数据查询语言用来查询数据库中表的记录查询关键字:SELECTDQL-语法DQL-条件查询语法:SELECT字段列表FROM表名WHERE条件列表;条件:LIKE查询(模糊查询)%包含零个或多个字符的任意字符串_(下划线)任何单个字符......
  • Linux的shell脚本中的比较运算符
    shell中的比较运算符-eq    //等于-ne    //不等于-gt    //大于(greater)-lt     //小于 (less)-ge    //大于等于-le    //小于等于在今天的Linux——shell命令实验中,执行.sh脚本:if((a<60));thenecho"Youdidn'tpassthe......
  • linux 每隔多少秒执行定时任务
    Linux中的定时任务通常使用crontab来实现,但crontab的最小时间单位是分钟,因此默认情况下无法实现一分钟内多次执行的定时任务。不过,你可以使用以下方法之一来实现一分钟内多次执行的效果:编写一个无限循环的脚本:你可以编写一个无限循环的脚本,并在其中添加需要重复执行的命令......