强化条件变量类模板
封装强化条件变量,替代C++标准库的条件变量,应对激活先于阻塞问题。
设计原理
同步设计
条件变量只激活进入等待队列的线程,无法确保激活在阻塞之后,存在激活先于阻塞问题。其影响不可忽视,特别是精确调度。
结合条件变量、谓词和临界区即可解决问题。以谓词决定阻塞和激活,两种操作分别置于临界区,确保操作互斥。
采用上述解决方案,只存在两种情况,一种是激活在阻塞之后,另一种是谓词为真而线程不必阻塞。
关于激活先于阻塞,更多介绍见章节——程序设计技巧之激活先于阻塞。
互斥策略
互斥策略主要用于优化无谓词函数,包括严格互斥策略和宽松互斥策略,默认采用严格互斥策略。
- 严格互斥策略适用于无谓词函数,锁定互斥元之后激活线程,确保操作的原子性。
- 宽松互斥策略适用于带谓词函数,先锁定并解锁互斥元,再激活线程,避免线程因锁定互斥元而再次阻塞,从而减少线程的上下文切换次数。
文件依赖性
强化条件变量类模板不依赖标准库以外的文件,其定义于头文件Condition.hpp。
Condition.hpp
- GitCode: Condition.hpp
- Gitee: Condition.hpp
- GitHub: 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行:定义互斥策略枚举。
成员变量
强化条件变量类模板的成员变量如表所示:
变量名 | 类型 | 说明 |
---|---|---|
_mutex | std::mutex | 互斥元 |
_validity | std::atomic_bool | 有效性标志 |
_condition | std::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行:返回谓词是否为真。