C++17 强化条件变量类模板


封装强化条件变量,替代C++标准库的条件变量,应对激活先于阻塞问题。

设计原理

同步设计

条件变量只激活进入等待队列的线程,无法确保激活在阻塞之后,存在激活先于阻塞问题。其影响不可忽视,特别是精确调度。

结合条件变量、谓词和临界区即可解决问题。以谓词决定阻塞和激活,两种操作分别置于临界区,确保操作互斥。

采用上述解决方案,只存在两种情况,一种是激活在阻塞之后,另一种是谓词为真而线程不必阻塞。

关于激活先于阻塞,更多介绍见章节——程序设计技巧之激活先于阻塞。

互斥策略

互斥策略主要用于优化无谓词函数,包括严格互斥策略和宽松互斥策略,默认采用严格互斥策略。

  1. 严格互斥策略适用于无谓词函数,锁定互斥元之后激活线程,确保操作的原子性。
  2. 宽松互斥策略适用于带谓词函数,先锁定并解锁互斥元,再激活线程,避免线程因锁定互斥元而再次阻塞,从而减少线程的上下文切换次数。

文件依赖性

强化条件变量类模板不依赖标准库以外的文件,其定义于头文件Condition.hpp。

Condition.hpp

类定义

强化条件变量类模板的定义如下所示:

#pragma once

#include <chrono>
#include <cstddef>
#include <cstdint>
#include <atomic>
#include <condition_variable>
#include <mutex>

template <typename _Size = std::size_t>
class Condition final
{
public:
	enum class Policy : std::uint8_t
	{
		STRICT, RELAXED
	};

public:
	using Size = _Size;

private:
	std::mutex _mutex;
	std::atomic_bool _validity;
	std::condition_variable _condition;

public:
	Condition() : _validity(true) {}

	Condition(const Condition&) = delete;

	~Condition() { exit(); }

	Condition& operator=(const Condition&) = delete;

	explicit operator bool() const noexcept { return valid(); }

	bool valid() const noexcept
	{
		return _validity.load(std::memory_order_relaxed);
	}

	void enter() noexcept
	{
		_validity.store(true, std::memory_order_relaxed);
	}

	void exit();

	void notify_one(Policy _policy = Policy::STRICT);

	void notify_all(Policy _policy = Policy::STRICT);

	void notify(Size _size, Policy _policy = Policy::STRICT);

	template <typename _Predicate>
	void notify_one(_Predicate _predicate);

	template <typename _Predicate>
	void notify_all(_Predicate _predicate);

	template <typename _Predicate>
	void notify(Size _size, _Predicate _predicate);

	void wait();

	template <typename _Predicate>
	void wait(_Predicate _predicate);

	template <typename _Rep, typename _Period>
	bool wait_for(const std::chrono::duration<_Rep, _Period>& _relative);

	template <typename _Rep, typename _Period, typename _Predicate>
	bool wait_for(const std::chrono::duration<_Rep, _Period>& _relative, \
		_Predicate _predicate);

	template <typename _Clock, typename _Duration>
	bool wait_until(const std::chrono::time_point<_Clock, _Duration>& _absolute);

	template <typename _Clock, typename _Duration, typename _Predicate>
	bool wait_until(const std::chrono::time_point<_Clock, _Duration>& _absolute, \
		_Predicate _predicate);
};

代码解析

  • 第14-17行:定义互斥策略枚举。

成员变量

强化条件变量类模板的成员变量如表所示:

变量名类型说明
_mutexstd::mutex互斥元
_validitystd::atomic_bool有效性标志
_conditionstd::condition_variable条件变量

条件变量仍然负责阻塞和激活线程。互斥元用于构建临界区,确保操作的原子性。而有效性标志是阻塞条件之一,同时作为线程的循环条件。

成员函数

结束所有线程

结束线程是精确调度方式,旨在防止激活先于阻塞,确保线程正常结束。

template <typename _Size>
void Condition<_Size>::exit()
{
	std::unique_lock lock(_mutex);
	if (!valid()) return;

	_validity.store(false, \
		std::memory_order_relaxed);
	lock.unlock();

	_condition.notify_all();
}

代码解析

  • 第4,9行:构建临界区,确保判断有效性与设置有效性是原子操作。
  • 第7,8行:有效实例转为无效状态,确保只激活一次所有线程。
  • 第11行:激活所有线程。

根据策略激活单线程

template <typename _Size>
void Condition<_Size>::notify_one(Policy _policy)
{
	std::unique_lock lock(_mutex);
	if (_policy == Policy::RELAXED) lock.unlock();

	_condition.notify_one();
}

代码解析

  • 第5行:倘若采用宽松互斥策略,在激活线程之前解锁互斥元。

根据策略激活所有线程

template <typename _Size>
void Condition<_Size>::notify_all(Policy _policy)
{
	std::unique_lock lock(_mutex);
	if (_policy == Policy::RELAXED) lock.unlock();

	_condition.notify_all();
}

根据策略激活多线程

template <typename _Size>
void Condition<_Size>::notify(Size _size, \
	Policy _policy)
{
	std::unique_lock lock(_mutex);
	if (_policy == Policy::RELAXED)
		lock.unlock();

	for (decltype(_size) index = 0; \
		index < _size; ++index)
		_condition.notify_one();
}

代码解析

  • 第9-11行:进行指定次数的激活单线程操作。

根据谓词激活单线程

template <typename _Size>
template <typename _Predicate>
void Condition<_Size>::notify_one(_Predicate _predicate)
{
	std::unique_lock lock(_mutex);
	if (!_predicate()) return;
	lock.unlock();

	_condition.notify_one();
}

代码解析

  • 第7-9行:倘若谓词为真,先解锁互斥元,再激活单线程。避免线程因锁定未解锁的互斥元而再次阻塞,减少线程的上下文切换次数。

根据谓词激活所有线程

template <typename _Size>
template <typename _Predicate>
void Condition<_Size>::notify_all(_Predicate _predicate)
{
	std::unique_lock lock(_mutex);
	if (!_predicate()) return;
	lock.unlock();

	_condition.notify_all();
}

根据谓词激活多线程

template <typename _Size>
template <typename _Predicate>
void Condition<_Size>::notify(Size _size, \
	_Predicate _predicate)
{
	std::unique_lock lock(_mutex);
	if (!_predicate()) return;
	lock.unlock();

	for (decltype(_size) index = 0; \
		index < _size; ++index)
		_condition.notify_one();
}

阻塞线程

template <typename _Size>
void Condition<_Size>::wait()
{
	std::unique_lock lock(_mutex);
	if (valid())
		_condition.wait(lock);
}

代码解析

  • 第5,6行:倘若实例有效,阻塞线程,否则不必阻塞。

根据谓词阻塞线程

template <typename _Size>
template <typename _Predicate>
void Condition<_Size>::wait(_Predicate _predicate)
{
	std::unique_lock lock(_mutex);
	while (valid() && !_predicate())
		_condition.wait(lock);
}

代码解析

  • 第6,7行:若实例有效且谓词为假,则阻塞线程,直到实例无效或者谓词为真,才能够被激活,以防止虚假激活。

虚假激活见章节——程序设计技巧之虚假激活。

阻塞线程一定时长

template <typename _Size>
template <typename _Rep, typename _Period>
bool Condition<_Size>::wait_for(const std::chrono::duration<_Rep, _Period>& _relative)
{
	std::unique_lock lock(_mutex);
	return valid() \
		&& _condition.wait_for(lock, _relative) == std::cv_status::no_timeout;
}

代码解析

  • 第6,7行:若实例有效,则阻塞线程,一定时长之后激活线程,返回是否超时。

根据谓词阻塞线程一定时长

template <typename _Size>
template <typename _Rep, typename _Period, typename _Predicate>
bool Condition<_Size>::wait_for(const std::chrono::duration<_Rep, _Period>& _relative, \
	_Predicate _predicate)
{
	std::unique_lock lock(_mutex);
	return _condition.wait_for(lock, _relative, \
		[this, &_predicate] { return !valid() || _predicate(); });
}

代码解析

  • 第7,8行:若实例有效且谓词为假,则阻塞线程,直到实例无效或者谓词为真,才能够被激活。

阻塞线程到指定时间

template <typename _Size>
template <typename _Clock, typename _Duration>
bool Condition<_Size>::wait_until(const std::chrono::time_point<_Clock, _Duration>& _absolute)
{
	std::unique_lock lock(_mutex);
	return valid() \
		&& _condition.wait_until(lock, _absolute) == std::cv_status::no_timeout;
}

根据谓词阻塞线程到指定时间

template <typename _Size>
template <typename _Clock, typename _Duration, typename _Predicate>
bool Condition<_Size>::wait_until(const std::chrono::time_point<_Clock, _Duration>& _absolute, \
	_Predicate _predicate)
{
	std::unique_lock lock(_mutex);
	while (valid() && !_predicate() \
		&& _condition.wait_until(lock, _absolute) != std::cv_status::timeout);
	return _predicate();
}

代码解析

  • 第7,8行:若实例有效且谓词为假,则阻塞线程,待到实例无效或者谓词为真,被另一线程激活,或者因超时而激活线程。
  • 第9行:返回谓词是否为真。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值