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 的三种方式

  1. std::async()
  2. std::package_task<> 模板类
  3. 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);

  1. 返回值,布尔类型,若 atomic_value == expected_value完成了保存 the_other_value 到 atomic_value 中这一保存动作,返回值为 true;否则,其他情况下,返回 false
  2. atomic_value,原子变量,函数的调用者
  3. expected_value,使用者给定的期望值,用于和 atomic_value 比较,如果相等则将 the_other_value 存入 atomic_value;否则,将 atomic_value 的值赋予 expected_value
  4. 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

  1. 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 中要被剪切的元素的范围。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值