首页 > 其他分享 >线程同步之条件变量

线程同步之条件变量

时间:2024-01-20 09:55:44浏览次数:26  
标签:std 同步 变量 线程 variable cv condition wait

目录


condition_variable简介

std::condition_variable是C++中用于线程同步的一个类。它通常与std::mutex一起使用,用于在一个或多个线程中阻塞,直到另一个线程修改了共享变量并通知了condition_variable。下面是一个例子,演示了如何使用std::condition_variable来实现线程间的通信:

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

std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;

void worker_thread() {
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, [] { return ready; });
    std::cout << "Worker thread is processing data\n";
    data += " after processing";
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";
    lk.unlock();
    cv.notify_one();
}

int main() {
    std::thread worker(worker_thread);

    {
        std::lock_guard<std::mutex> lk(m);
        ready = true;
    }
    cv.notify_one();

    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, [] { return processed; });
    }
    std::cout << "Back in main, data = " << data << '\n';

    worker.join();
}

在这个例子中,worker_thread等待ready变量为true,然后处理数据并通知主线程。主线程等待processed变量为true,然后打印处理后的数据。这个例子展示了std::condition_variable的基本用法。


成员函数

std::condition_variable是C++标准库中用于线程同步的类,它提供了以下成员函数:

  1. wait:阻塞当前线程,直到收到通知或超时。

    void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
    

    这个函数会在持有std::unique_lock锁的情况下,释放锁并将线程置于等待状态。当收到通知或超时时,线程会重新获得锁并继续执行。

    在使用std::condition_variablewait函数时,可以正确地传递Predicate参数。wait函数有两个重载版本,其中第二个版本接受一个Predicate参数,用于判断条件是否满足。Predicate是一个可调用对象,通常是一个lambda表达式或者函数对象,用于在等待期间检查条件是否满足。

    下面是一个示例,演示了如何在wait函数中正确地传递Predicate参数:

    std::condition_variable cv;
    std::mutex mtx;
    bool dataReady = false;
    
    void threadFunction() {
        std::unique_lock<std::mutex> lck(mtx);
        cv.wait(lck, []{ return dataReady; });
        // 线程被唤醒后,继续执行其他操作
    }
    
    void setDataReady() {
        std::lock_guard<std::mutex> lck(mtx);
        dataReady = true;
        cv.notify_one();
    }
    

    在这个示例中,cv.wait函数的第二个参数是一个lambda表达式[]{ return dataReady; },用于检查条件dataReady是否为true。当条件满足时,线程会被唤醒并继续执行。

    因此,正确地传递Predicate参数就是在cv.wait函数中使用一个可调用对象(通常是lambda表达式或者函数对象),用于检查条件是否满足。

  2. notify_one:通知一个等待的线程。

    void notify_one();
    

    这个函数用于通知一个正在等待的线程,使其从等待状态中唤醒。

  3. notify_all:通知所有等待的线程。

    void notify_all();
    

    这个函数用于通知所有正在等待的线程,使它们从等待状态中唤醒。

这些成员函数可以与std::mutex一起使用,用于在一个或多个线程中阻塞,直到另一个线程修改了共享变量并通知了condition_variablewait函数会释放互斥锁,并在收到通知后重新获取互斥锁,从而实现线程的同步和通信。这些成员函数提供了一种有效的方式来协调多个线程的操作,以实现线程间的同步和通信。


实现线程间的通信

当使用std::condition_variable时,通常会结合使用std::mutex来实现线程间的通信。下面是一个实际的例子,演示了如何使用std::condition_variable来实现生产者-消费者模式,实现线程间的同步和通信。

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

std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;

void producer() {
    for (int i = 0; i < 10; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        {
            std::lock_guard<std::mutex> lk(mtx);
            data_queue.push(i);
            std::cout << "Produced: " << i << std::endl;
        }
        cv.notify_one();
    }
}

void consumer() {
    for (int i = 0; i < 10; ++i) {
        std::unique_lock<std::mutex> lk(mtx);
        cv.wait(lk, [] { return !data_queue.empty(); });
        int data = data_queue.front();
        data_queue.pop();
        std::cout << "Consumed: " << data << std::endl;
    }
}

int main() {
    std::thread producer_thread(producer);
    std::thread consumer_thread(consumer);

    producer_thread.join();
    consumer_thread.join();

    return 0;
}

在这个例子中,producer线程向data_queue中推送数据,而consumer线程从data_queue中取出数据。std::condition_variable用于在consumer线程中等待,直到data_queue中有数据。这个例子展示了如何使用std::condition_variable来实现线程间的通信,以及如何在多线程环境中实现同步。

标签:std,同步,变量,线程,variable,cv,condition,wait
From: https://www.cnblogs.com/yubo-guan/p/17976069

相关文章

  • C++11原子变量:线程安全、无锁操作的实例解析
     在C++11中,原子变量(std::atomic)提供了一种线程安全的方式来操作共享变量。下面是一个简单的例子,演示了C++11原子变量的用法。#include<iostream>#include<atomic>#include<thread>std::atomic<int>counter(0);//声明一个原子整数变量voidincrementCounter(int......
  • 无涯教程-MATLAB - 变量声明
    在MATLAB环境中,每个变量都是一个数组或矩阵。您可以通过简单的方式分配变量。例如,x=3 %定义x并用一个值初始化它MATLAB将执行上述语句并返回以下输出-x=3它创建一个名为x的1-by-1矩阵,并将值3存储在其元素中,让我们再看一个例子,x=sqrt(16) %定义x并用表达式......
  • Java多线程
    Java多线程名词解释程序(program)是为完成特定任务、用某种语言编写的一组指令集合。简单而言:就是自己写的代码进程(Process)进程是指运行中的程序,比如启动迅雷时,就启动了一个进程,操作系统就会为该进程分配内存空间。进程是程序的一次执行过程,或是正在运行的一个程序。是......
  • JavaScript保留字和预定义的全局变量及函数汇总
    保留字也称关键字,每种语言中都有该语言本身规定的一些关键字,这些关键字都是该语言的语法实现基础,JavaScript中规定了一些标识符作为现行版本的关键字或者将来版本中可能会用到的关键字,所以当我们定义标识符时就不能使用这些关键字了,下面介绍下JavaScript保留字和预定义的全局变量......
  • 线程池
    ExecutorServiceexecutor=Executors.newCachedThreadPool();ExecutorServiceexecutor2=Executors.newFixedThreadPool();ExecutorServiceexecuto3=Executors.newSingleThreadExecutor();SynchronousQueue和LinkedBlockingQueue是Java中两种不同类型的阻塞队列,它们在实现......
  • 时间同步NTP和chronyc
    chronyc手动同步要进行手动同步,可以使用chronyc命令。以下是一些基本的使用方法:启动chronyd服务:在Linux系统中,可以通过运行systemctlenablechronyd.service和systemctlstartchronyd.service来启用并启动chronyd服务。1编辑配置文件:在需要同步的系统上,打开配置文件/etc......
  • java线程的基本操作
    1.线程名称的设置和获取在Thread类中可以通过构造器Thread(...)初始化设置线程名称,也可以通过setName(...)实例方法去设置线程名称,取得线程名称可以通过getName()方法完成。关于线程名称有以下几个要点:线程名称一般在启动线程前设置,但也允许为运行的线程设置名称......
  • elasticsearch学习笔记2 - logstash jdbc 同步MYSQL到ES
    logstash是一个管道工具input输入output输出filter过滤条件咋们初级先了解到这些再说比较优秀的教程文档作为基础知识需要了解:https://blog.csdn.net/qq_19283249/article/details/130839158?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170564710516800215......
  • 线程和进程
    进程和线程是操作系统中的两个基本概念,他们都是用来完成执行任务的,但是有所区别。进程是资源分配的最小单位,它代表CPU所能处理的单个任务。每个进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段。而线程是进程中执......
  • (8)Powershell中变量的定义和使用
    (8)Powershell中变量的定义和使用这一节主要介绍Powershell中变量的定义和使用,以及使用变量时应该注意的事项。和所有编程语言中的变量一样,Powershell中的变量也是存于存储值的内存单元,需要注意的是,Powershell中的变量是以美元符号($)开头的单字节(一般是英文字符,虽然ISE中支持......