同步并发操作之等待一个事件或条件

两个线程需要同步操作时,可以设置一个全局变量,用互斥量保护这个全局变量,通过这个全局变量来同步。

但是这样太浪费CPU,这时可以用休眠方法。

bool flag;
std::mutex m;

void wait_for_flag()
{
	std::unique_lock<std::mutex> lk(m);
	while(!flag)
	{
		lk.unlock();
		//休眠期间,其他线程可以操作flag
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
		lk.lock();
	}
}

但是很难确定休眠时间的长短,太长或太短都不合理。

在C++库中,可以使用条件变量(condition variable)。条件变量可以和事件或条件结合起来,一个或多个线程等待时间或条件。当事件到达货事件满足后,使用nofify通知等待的线程。

下面看一个例子,生产者号消费者:

#include<iostream>
#include<mutex>
#include<condition_variable>
#include<queue>
#include<thread>
#include<Windows.h>//VS2013调试用的

std::mutex mut;
std::queue<int> q;
std::condition_variable cond;

void data_preparation_thread()
{
	while (true)
	{
		//休眠一下
		Sleep(1000);//1s
		int data = rand() % 20;
		std::lock_guard<std::mutex> lk(mut);
		q.push(data);
		std::cout << "Preparation Data:" << data << std::endl;
		cond.notify_one();//唤醒一个,notify_any()唤醒所有
	}
}

void data_processing_thread()
{
	while (true)
	{
		Sleep(2000);//2s
		std::unique_lock<std::mutex> lk(mut);
		/*
		wait先检查lambda表达式,如果满足条件则返回。否则就等待,此时unlock mut然后线程阻塞。
		当线程被唤醒,会重新lock mut,并检查lambda表达式。如果lambda不满足,那么unlock mut继续等待。
		所以上面要有unique_lock
		*/
		cond.wait(lk, []{return !q.empty(); });//lambda function,等待的条件[]{return !q.empty(); }
		int data = q.front();
		q.pop();
		lk.unlock();
		std::cout << "Processing Data:" << data << std::endl;
	}
}
int main()
{
	std::thread t1(data_preparation_thread);
	std::thread t2(data_processing_thread);
	t1.join();
	t2.join();
	return 0;
}

下面使用条件变量编写一个线程安全的队列

#include<queue>
#include<condition_variable>
#include<mutex>
#include<memory>

template<typename T>
class threadsafe_queue
{
private:
	mutable std::mutex mut;//在const函数中使用
	std::queue<T> data_queue;
	std::condition_variable data_cond;
public:
	threadsafe_queue()
	{}
	threadsafe_queue(threadsafe_queue const& other)
	{
		std::lock_guard<std::mutex> lk(other.mut);
		data_queue = other.data_queue;
	}
	void push(T new_value)
	{
		std::lock_guard<std::mutex> lk(mut);
		data_queue.push(new_value);
		data_cond.notify_one();//如果多个线程等待,可以使用nofity_all()
	}
	void wait_and_pop(T& value)
	{
		std::unique_lock<std::mutex> lk(mut);
		data_cond.wait(lk, [this]{return !data_queue.empty(); });
		valuue = data_queue.front();
		data_queue.pop();
	}

	std::shared_ptr<T> wait_and_pop()
	{
		std::unique_lock<std::mutex> lk(mut);
		data_cond.wait(lk, [this]{return !data_queue.empty(); });
		std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
		data_queue.pop();
		return res;
	}

	bool try_pop(T& value)
	{
		std::lock_guard<std::mutex> lk(mut);
		if (data_queue.empty())
			return false;
		value = data_queue.front();
		data_queue.pop();
		return true;
	}
	std::shared_ptr<T> try_pop()
	{
		std::lock_guard<std::mutex> lk(mut);
		if (data_queue.empty())
			return std::shared_ptr<T>();
		std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
		data_queue.pop();
		return res;
	}
	bool empty() const
	{
		std::lock_guard<std::mutex> lk(mut);
		return data_queue.empty();
	}

};
时间: 2024-11-13 10:31:42

同步并发操作之等待一个事件或条件的相关文章

同步并发操作之等待一次性事件

有时候需要用一些后台线程来完成计算,这些计算往往都是一次性的,线程计算完后便结束.这时候可以使用条件变量,但是有点浪费,我们只需要获取一次结果.C++标准库中有头文件<future>,很形象"未来",获取未来计算的结果. 使用std::async来启动一个异步任务.用std::future对象来存储异步任务返回的结果,这个对象存储结果.当我们需要结果时,只需调用get()方法.这时如果还没计算完毕,当前线程会阻塞. #include<future> #inclu

Cpp Concurrency In Action(读书笔记3)——同步并发操作

等待一个事件或其他条件 第一,它可以持续的检查共享数据标志(用于做保护工作的互斥量),直到另一线程完成工作时对这个标志进行重设. 第二个选择是在等待线程在检查间隙,使用 std::this_thread::sleep_for() 进行周期性的间歇: #include <iostream> #include <thread> #include <mutex> class wait_test { bool flag; std::mutex m; public: wait_t

《C++ Concurrency in Action》读书笔记三 同步并发操作

本章要点 *等待事件 *使用futures等待一次性事件(waiting for one-off events with futures) *等待时间限制 *使用同步操作来简化代码 这章主要描述了如何使用条件变量和futures来等待事件,以及如何使用他们来使线程同步操作更加简化. CP4 1. 等待事件或者其他条件 a.如果一个线程需要等待另外一个线程处理的结果,可以采取不停的检测共享资源,等另外一个线程完成特定操作以后会修改共享资源.该线程检测到修改以后执行后续的操作,但这种方法需要不停的检

并发操作的一致性问题

2.2.1 并发一致性问题 常见并发并发一致性问题包括:丢失的修改.不可重复读.读脏数据.幻影读(幻影读在一些资料中往往与不可重复读归为一类). 2.2.1.1 丢失修改 下面我们先来看一个例子,说明并发操作带来的数据的不一致性问题. 考虑飞机订票系统中的一个活动序列: 甲售票点(甲事务)读出某航班的机票余额A,设A=16. 乙售票点(乙事务)读出同一航班的机票余额A,也为16. 甲售票点卖出一张机票,修改余额A←A-1.所以A为15,把A写回数据库. 乙售票点也卖出一张机票,修改余额A←A-1

数据库中的并发操作带来的一系列问题及解决方法

数据库中常见的并发操作所带来的一致性问题包括:丢失的修改.不可重复读.读脏数据.幻影读(幻影读在一些资料中往往与不可重复读归为一类). 丢失修改 下面我们先来看一个例子,说明并发操作带来的数据的不一致性问题. 考虑飞机订票系统中的一个活动序列: 甲售票点(甲事务)读出某航班的机票余额A,设A=16. 乙售票点(乙事务)读出同一航班的机票余额A,也为16. 甲售票点卖出一张机票,修改余额A←A-1.所以A为15,把A写回数据库. 乙售票点也卖出一张机票,修改余额A←A-1.所以A为15,把A写回数

数据库的并发操作

数据库的并发操作 事务 事务(Transaction)是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位. 事务是恢复和并发控制的基本单位 事务的ACID特性: 原子性(Atomicity):事务是数据库的逻辑工作单位 一致性(Consistency):事务执行的结果必须是使数据库从一个一致性状态变 到另一个一致性状态 隔离性(Isolation):一个事务的执行不能被其他事务干扰 持续性(Durability ):一个事务一旦提交,它对数据库中数据的改变就应

[C++11 并发编程] 13 使用期望等待一次性事件

C++标准库使用期望(future)来支持一次性事件的等待.要等待某种一次性事件的线程可以获取一个代表该该事件的期望.这个线程可以每隔一段事件周期性的查询这个期望.此外,这个线程也可以继续做其它的处理,直到需要等待这个一次性事件才被挂起.通过期望还可以可以传递数据. C++标准库提供了两种期望unique future(std::future<>)和shared futures(std::shared_future<>),都声明在<future>库头文件中.std::f

Python3标准库:threading进程中管理并发操作

1. threading进程中管理并发操作 threading模块提供了管理多个线程执行的API,允许程序在同一个进程空间并发的运行多个操作. 1.1 Thread对象 要使用Thread,最简单的方法就是用一个目标函数实例化一个Thread对象,并调用start()让它开始工作. import threading def worker(): """thread worker function""" print('Worker') threads

同步、异步、多线程与事件型综述

转自:http://blog.csdn.net/chszs/article/details/8867174 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs首先要了解什么是阻塞和阻塞式IO.线程在执行中如果遇到磁盘读写或网络通信(统称IO操作),通常要耗费较长的时间,这时操作系统会剥夺此线程的CPU控制权,使其暂停执行,同时将资源让给其他的工作线程,这种线程调度方式称为阻塞.当IO操作完毕时,操作系统将这个线程的阻塞状态解除,恢复其对CPU的控制权,令