C++多线程学习记录
Functions
preface
#define printv(value) \
std::cout << #value << "--->" << value << std::endl
template<typename Container>
void printc(const Container& container)
{
for (auto cIter = container.cbegin(); cIter != container.cend(); ++cIter)
std::cout << *cIter << ", ";
std::cout << "\b\b";
std::cout << ' ';
std::cout << std::endl;
}
----------------------------------------------------------------------------------
#define _EXPORT_STD
// P0607R0 Inline Variables For The STL
#if _HAS_CXX17
#define _INLINE_VAR inline
#else // _HAS_CXX17
#define _INLINE_VAR
#endif // _HAS_CXX17
线程睡眠一段时间
std::this_thread::sleep_for(std::chrono::seconds(3));
Conceptions Understanding
条件变量 std::condition_variable
调用 wait() 函数时需传入一个已经锁住的锁对象,随后在内部调用传入的可调用对象进行条件判断。若条件成立,则 wait() 函数立即返回;否则,wait() 函数在内部解锁互斥,并令线程进入阻塞状态或等待状态。此时若接收到其他线程关于此条件变量的 notify_one() 或 notify_all() 等 notify 系列信号,则此线程从休眠中觉醒,重新在互斥量上获取锁后进行条件查验:若条件成立则 wait() 返回,此时互斥量仍被锁住;若条件不成立,则此线程解锁互斥量并继续休眠。
void data_processing_thread()
{
while(true)
{
std::unique_lock<std::mutex> lk(mut);
data_cond.wait(lk,[]{return !data_queue.empty();});
data_chunk data=data_queue.front();
data_queue.pop();
lk.unlock();
process(data);
if(is_last_chunk(data))
break;
}
}
std::future
创建 future 的三种方式
- std::async()
- std::package_task<> 模板类
- std::promise
std::async()
std::package_task<> 模板类
std::promise<> 模板类
官方注释: class that defines an asynchronous provider that holds a value
std::promise<> 模板类接收一个类型模板参数 T 作为保存结果的类型,并与一个接收同样类型模板参数的 std::future<> 模板类关联。std::promise<> 对象在一个线程中通过 set_value() 成员函数设置好值后,std::future<> 在另一个线程中就可以通过成员函数 get() 获取到该值。若 future 获取值时关联的 promise 还未设置值,则调用 get() 的线程将阻塞直到 promise 设置好值。
void hugeComputationProcedure(std::promise<int>& prm)
{
std::cout << "begin doing calculations..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "calculations done." << std::endl;
prm.set_value(10086);
}
int main()
{
std::promise<int> prm;
std::future<int> fut = prm.get_future();
std::thread tcal(hugeComputationProcedure, std::ref(prm));
tcal.detach();
std::cout << "main thread begins doing some work..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "main thread work done, and try to get tcal value..." << std::endl;
printv(fut.get());
return 0;
}
----------------------------------------------------------------------------------------------------------
main thread begins doing some work...
begin doing calculations...[此后停顿 3 秒]
main thread work done, and try to get tcal value...
fut.get()--->[此间停顿 2 秒]calculations done.
10086
原子类型 atomic<>
通过原子类型实现原子操作,原子操作是不可分割的操作(atomic operation is indivisible operation)。
std::atomic<int> ai;
printv(ai.is_lock_free());
printv(ai);
ai.store(10086);
printv(ai);
printv(ai.load());
printv(ai.exchange(10010));
printv(ai);
---------------------------------------------------------------------------------
ai.is_lock_free()--->1
ai--->0
ai--->10086
ai.load()--->10086
ai.exchange(10010)--->10086
ai--->10010
atomic<> 包括的成员函数(原子操作)有:
is_lock_free()
判定某一给定类型上的操作是能由原子指令直接实现(返回 true),还是要借助编译器和程序库的内部锁来实现(返回 false)。
store()
用来向原子变量赋值
load()
用来读取原子变量的值
exchange()
“读-改-写”操作,将原子变量设置成新值并返回旧值
compare_exchange_weak() & compare_exchange_strong()
bool res = atomic_value.compare_exchange_strong(expected_value, the_other_value);
- 返回值,布尔类型,若 atomic_value == expected_value 且 完成了保存 the_other_value 到 atomic_value 中这一保存动作,返回值为 true;否则,其他情况下,返回 false
- atomic_value,原子变量,函数的调用者
- expected_value,使用者给定的期望值,用于和 atomic_value 比较,如果相等则将 the_other_value 存入 atomic_value;否则,将 atomic_value 的值赋予 expected_value
- the_other_value,此处可视为常量,整个操作下来唯一不会变动的值
Extendance
理解 integral_constant
typedef std::integral_constant<int, 10086> I10086;
printv(I10086::value);
std::integral_constant<int, 10086> i10086;
printv(i10086.value);
printv(i10086 + 10010);
printv(i10086);
printv(i10086());
std::integral_constant<int, 10086> ii = i10086;
std::integral_constant<int, 10086> ic(i10086);
printv(ii);
printv(ic);
std::integral_constant<double, 1.0086> id;
printv(id);
// std::integral_constant<int, 10010> ij = i10086;
// error C2440: “初始化”: 无法从“std::integral_constant<int,10086>”转换为
// “std::integral_constant<int,10010>”
-------------------------------------------------------------------------------
I10086::value--->10086
i10086.value--->10086
i10086 + 10010--->20096
i10086--->10086
i10086()--->10086
ii--->10086
ic--->10086
id--->1.0086
整合常量类型,模板类,将一个常量(一个数或者其他什么量,整数布尔值浮点数云云)封装为一个类型,用此类型声明一个对象,此类实现了类型转换操作符和调用操作符。
template <class _Ty, _Ty _Val>
struct integral_constant {
static constexpr _Ty value = _Val;
/****************************************************************
we can provide in-class initializers for static members that
have const integral type and must do so for static members
that are constexprs of literal type. -- C++ Primer
****************************************************************/
using value_type = _Ty;
using type = integral_constant;
constexpr operator value_type() const noexcept {
return value;
}
_NODISCARD constexpr value_type operator()() const noexcept {
return value;
}
};
理解 true_type 和 false_type ( bool_constant )
todo…
_EXPORT_STD template <bool _Val>
using bool_constant = integral_constant<bool, _Val>;
_EXPORT_STD using true_type = bool_constant<true>;
_EXPORT_STD using false_type = bool_constant<false>;
理解 enable_if
模板类 enable_if 基于模板参数 _Test ,若为 true 则选择 enable_if 的偏特化版本,其中定义了内置类型 type 类型为另一个模板参数 _Ty 类型;若为 false 则什么也不做,此时若代码访问其中的 type 则会在编译期报错。
enable_if_t 提供了判定并使用模板参数类型的一种便捷方式。
_EXPORT_STD template <bool _Test, class _Ty = void>
struct enable_if {}; // no member "type" when !_Test
template <class _Ty>
struct enable_if<true, _Ty> { // type is _Ty for _Test
using type = _Ty;
};
_EXPORT_STD template <bool _Test, class _Ty = void>
using enable_if_t = typename enable_if<_Test, _Ty>::type;
理解 is_array_v
官方注释:determine whether type argument is an array
模板变量 is_array_v 接收一个模板参数,若参数具有数组型别,无论是否具有明确的大小,都会选择模板变量的偏特化版本从而使变量值为 true,否则变量值为 false。
EXPORT_STD template <class>
_INLINE_VAR constexpr bool is_array_v = false;
template <class _Ty, size_t _Nx>
_INLINE_VAR constexpr bool is_array_v<_Ty[_Nx]> = true;
template <class _Ty>
_INLINE_VAR constexpr bool is_array_v<_Ty[]> = true;
_EXPORT_STD template <class _Ty>
struct is_array : bool_constant<is_array_v<_Ty>> {};
理解 make_shared (基于入参构造函数参数列表) (C++20)
std::shared_ptr<std::vector<char> > sp_cvector =
std::make_shared<std::vector<char> >(11, 'c');
printc(*sp_cvector);
----------------------------------------------------------------------------------
c, c, c, c, c, c, c, c, c, c, c
模板函数 make_shared 接收待构建类型 _Ty 的构造函数参数 _Types,构造对象后返回指向对象的智能指针。make_shared 的返回值利用模板类 enable_if_t 保证其类型不是内置数组类型,否则在编译期即报错。
template <class _Ty, class... _Types>
enable_if_t<!is_array_v<_Ty>, shared_ptr<_Ty>>
make_shared(_Types&&... _Args) { // make a shared_ptr to non-array object
const auto _Rx = new _Ref_count_obj2<_Ty>(_STD forward<_Types>(_Args)...);
shared_ptr<_Ty> _Ret;
_Ret._Set_ptr_rep_and_enable_shared(_STD addressof(_Rx->_Storage._Value), _Rx);
return _Ret;
}
Container Algorithm
std::partition()
对序列中指定区间内的元素进行重新排列,使得符合指定判定条件的元素放到区间的前半段,不符合判定条件的元素放到区间的后半段,返回一个指向首个不符合条件元素的迭代器作为边界。此算法不保证序列指定区间内元素的初始相对位置。
std::list<int> ls;
for (int i = 0; i != 13; ++i)
ls.push_back(i);
auto ibound = std::partition(ls.begin(), ls.end(),
[](const int& i) { return i % 2 == 0; });
printc(ls);
printv(*ibound);
----------------------------------------------------------------------------------
0, 12, 2, 10, 4, 8, 6, 7, 5, 9, 3, 11, 1
*ibound--->7
Container Correlation
std::list
- splice() 函数,从目标 list 剪切元素到调用 list 上指定位置处
void splice (iterator pos, list& x);
void splice (iterator pos, list& x, iterator i);
void splice (iterator pos, list& x, iterator first, iterator last);
/* exam.
thisLs.splice(thisLs.begin(), srcLs, srcLs.begin(), srcLs.end());
*/
----------------------------------------------------------------------------------
posi:指向调用列表中要开始插入元素的位置的迭代器;
x:要被剪切元素的目标 list 引用;
i:指向目标 list 中要被剪切的单个元素的迭代器;
first 和 last:指定目标 list 中要被剪切的元素的范围。