跟我学c++中级篇——STL原子操作

240 篇文章 102 订阅

一、多线程同步

在操作系统中,多线程编程是目前一种主流的编程方式。如果一个程序没有用到多线程编程,那么这个程序自然就显得不高大上了。在很多有名的程序中,都号称使用了单线程,其实所谓单线程也只是针对它个主要的程序来说,在一些其它的处理机制上,可能仍然使用的是多线程。比如有名的Redist库,Node.js库等。避免使用多线程,主要是避免出现死锁,或者说避免资源的竞争导致整个程序在某种程度上的效率的下降。
但是,多线程编程自然会导致整个编程模型的复杂度极剧提高,作为不断涌现的新语言,当然不会视而不见,最主要的一种解决方式就是推出了协程这个概念。虽然说协程从某种意义上讲确实是大幅降低了编程的复杂度和难度。可是它仍然无法避免协程间资源的竞争(这里单纯指有栈协程)。
换句话说,资源的竞争才是真正的复杂的根本,那么有没有一种方法,可以不进行资源竞争,直接各个线程对资源进行访问而不需要考虑锁的问题呢?这个从根本意义上来讲是不可能存在的。但是工程的上的方式是有办法解决的。比如下面的原子库,使用原子库,自然就会大幅减少锁的粒度(尽量达到同一时间只有一个线程操作),尽量实现一种无锁编程。更或者,把锁的性质推移到硬件总线上去,同样实现一种无锁编程。
不管锁在还是不在,至少在应用层编程是不存在了,这才是根本。
简单的,就是真理。

二、STL中原子操作

原子操作在不同的语言中,有不同的实现,在STL中提供了如下的相关操作(https://en.cppreference.com/w/cpp/atomic):

在这里插入图片描述

在c++11到c++20整个发展的过程中,原子库也有较大的变化,这些细节可以查询相关的文档或者资料,不过以现在的c++编程在业界的推广速度,这点倒没有什么可太大担心的。毕竟,好多公司都放弃了c++,使用c++的,很多连c++11都没使用。
在STL中内存控制模型有以下几种:


std::memory_order
 C++ Atomic operations library 
Defined in header <atomic>
typedef enum memory_order {
    memory_order_relaxed,
    memory_order_consume,
    memory_order_acquire,
    memory_order_release,
    memory_order_acq_rel,
    memory_order_seq_cst
} memory_order;
(since C++11)
(until C++20)
enum class memory_order : /*unspecified*/ {
    relaxed, consume, acquire, release, acq_rel, seq_cst
};
inline constexpr memory_order memory_order_relaxed = memory_order::relaxed;
inline constexpr memory_order memory_order_consume = memory_order::consume;
inline constexpr memory_order memory_order_acquire = memory_order::acquire;
inline constexpr memory_order memory_order_release = memory_order::release;
inline constexpr memory_order memory_order_acq_rel = memory_order::acq_rel;
inline constexpr memory_order memory_order_seq_cst = memory_order::seq_cst;

其具体的解释见下面:

在这里插入图片描述

但是涉及原子操作相关的主要有:
1、 原子存储操作(store):memorey_order_relaxed、memory_order_release、memory_order_seq_cst。
2、 原子读取操作(load):memorey_order_relaxed、memory_order_consume、memory_order_acquire、memory_order_seq_cst。
3、 RMW操作(read-modify-write)(如atomic_compare_exchange):memorey_order_relaxed、memory_order_consume、memory_order_acquire、memory_order_release、memory_order_acq_rel、memory_order_seq_cst

三、源码分析

看一下原子库的相关源码:

  template<>
	struct atomic<_ITYPE>
		: _ATOMIC_ITYPE
	{	/* template specialization that manages
			values of _ITYPE atomically */
	using value_type = _ITYPE;
	using difference_type = _ITYPE;

	atomic() noexcept = default;

	constexpr atomic(_ITYPE _Val) noexcept
		: _ATOMIC_ITYPE{(_ATOMIC_UINT)_Val}
		{	// construct from _Val, initialization is not atomic
		}

	_ITYPE operator=(_ITYPE _Val) volatile noexcept
		{	// assign from _Val
		return (_ATOMIC_ITYPE::operator=(_Val));
		}

	_ITYPE operator=(_ITYPE _Val) noexcept
		{	// assign from _Val
		return (_ATOMIC_ITYPE::operator=(_Val));
		}

	atomic(const atomic&) = delete;
	atomic& operator=(const atomic&) = delete;
	atomic& operator=(const atomic&) volatile = delete;
	};
 #endif /* _ATOMIC_HAS_NO_SPECIALIZATION */

 #ifdef _ATOMIC_IS_ADDRESS_TYPE
		// STRUCT TEMPLATE PARTIAL SPECIALIZATION atomic<_Ty *>
template<class _Ty>
	struct atomic<_Ty *>
		: _Atomic_address
	{	// template that manages values of _Ty * atomically
	using value_type = _Ty *;
	using difference_type = ptrdiff_t;

	atomic(const atomic&) = delete;
	atomic& operator=(const atomic&) = delete;
	atomic& operator=(const atomic&) volatile = delete;

	atomic() noexcept = default;

	constexpr atomic(_Ty *_Right) noexcept
		: _Atomic_address{(_ATOMIC_UINT)_Right}
		{	// construct from _Right, initialization is not atomic
		}

	_Ty *operator=(_Ty *_Right) volatile noexcept
		{	// assign from _Right
		return (reinterpret_cast<_Ty *>(
			_Atomic_address::operator=((void *)_Right)));
		}

	_Ty *operator=(_Ty *_Right) noexcept
		{	// assign from _Right
		return (reinterpret_cast<_Ty *>(
			_Atomic_address::operator=((void *)_Right)));
		}

	void store(_Ty *_Value, memory_order _Order = memory_order_seq_cst) volatile noexcept
		{	// store _Value into *this
		_Atomic_address::store((void *)_Value, _Order);
		}

	void store(_Ty *_Value, memory_order _Order = memory_order_seq_cst) noexcept
		{	// store _Value into *this
		_Atomic_address::store((void *)_Value, _Order);
		}

	_NODISCARD _Ty *load(memory_order _Order = memory_order_seq_cst) const volatile noexcept
		{	// return value held in *this
		return (reinterpret_cast<_Ty *>(_Atomic_address::load(_Order)));
		}

	_NODISCARD _Ty *load(memory_order _Order = memory_order_seq_cst) const noexcept
		{	// return value held in *this
		return (reinterpret_cast<_Ty *>(_Atomic_address::load(_Order)));
		}

	operator _Ty *() const volatile noexcept
		{	// return value held in *this
		return (load());
		}

	operator _Ty *() const noexcept
		{	// return value held in *this
		return (load());
		}

	_Ty *exchange(_Ty *_Value, memory_order _Order = memory_order_seq_cst) volatile noexcept
		{	// exchange value stored in *this with _Value
		return (reinterpret_cast<_Ty *>(_Atomic_address::exchange(
			(void *)_Value, _Order)));
		}

	_Ty *exchange(_Ty *_Value, memory_order _Order = memory_order_seq_cst) noexcept
		{	// exchange value stored in *this with _Value
		return (reinterpret_cast<_Ty *>(_Atomic_address::exchange(
			(void *)_Value, _Order)));
		}

	bool compare_exchange_weak(
		_Ty *& _Exp, _Ty *_Value,
		memory_order _Order1, memory_order _Order2) volatile noexcept
		{	// compare and exchange value stored in *this with *_Exp, _Value
		return (_Atomic_address::compare_exchange_weak(
			(void *&)_Exp, (void *)_Value, _Order1, _Order2));
		}

	bool compare_exchange_weak(
		_Ty *& _Exp, _Ty *_Value,
		memory_order _Order1, memory_order _Order2) noexcept
		{	// compare and exchange value stored in *this with *_Exp, _Value
		return (_Atomic_address::compare_exchange_weak(
			(void *&)_Exp, (void *)_Value, _Order1, _Order2));
		}

	bool compare_exchange_weak(
		_Ty *& _Exp, _Ty *_Value,
		memory_order _Order = memory_order_seq_cst) volatile noexcept
		{	// compare and exchange value stored in *this with *_Exp, _Value
		return (_Atomic_address::compare_exchange_weak(
			(void *&)_Exp, (void *)_Value, _Order));
		}

	bool compare_exchange_weak(
		_Ty *& _Exp, _Ty *_Value,
		memory_order _Order = memory_order_seq_cst) noexcept
		{	// compare and exchange value stored in *this with *_Exp, _Value
		return (_Atomic_address::compare_exchange_weak(
			(void *&)_Exp, (void *)_Value, _Order));
		}

	bool compare_exchange_strong(
		_Ty *& _Exp, _Ty *_Value,
		memory_order _Order1, memory_order _Order2) volatile noexcept
		{	// compare and exchange value stored in *this with *_Exp, _Value
		return (_Atomic_address::compare_exchange_strong(
			(void *&)_Exp, (void *)_Value, _Order1, _Order2));
		}

	bool compare_exchange_strong(
		_Ty *& _Exp, _Ty *_Value,
		memory_order _Order1, memory_order _Order2) noexcept
		{	// compare and exchange value stored in *this with *_Exp, _Value
		return (_Atomic_address::compare_exchange_strong(
			(void *&)_Exp, (void *)_Value, _Order1, _Order2));
		}

	bool compare_exchange_strong(
		_Ty *& _Exp, _Ty *_Value,
		memory_order _Order = memory_order_seq_cst) volatile noexcept
		{	// compare and exchange value stored in *this with *_Exp, _Value
		return (_Atomic_address::compare_exchange_strong(
			(void *&)_Exp, (void *)_Value, _Order));
		}

	bool compare_exchange_strong(
		_Ty *& _Exp, _Ty *_Value,
		memory_order _Order = memory_order_seq_cst) noexcept
		{	// compare and exchange value stored in *this with *_Exp, _Value
		return (_Atomic_address::compare_exchange_strong(
			(void *&)_Exp, (void *)_Value, _Order));
		}

	_Ty *fetch_add(ptrdiff_t _Value, memory_order _Order = memory_order_seq_cst) volatile noexcept
		{	// add _Value to value stored in *this
		return (reinterpret_cast<_Ty *>(
			_Atomic_address::fetch_add(_Value * sizeof (_Ty), _Order)));
		}

	_Ty *fetch_add(ptrdiff_t _Value, memory_order _Order = memory_order_seq_cst) noexcept
		{	// add _Value to value stored in *this
		return (reinterpret_cast<_Ty *>(
			_Atomic_address::fetch_add(_Value * sizeof (_Ty), _Order)));
		}

	_Ty *fetch_sub(ptrdiff_t _Value, memory_order _Order = memory_order_seq_cst) volatile noexcept
		{	// subtract _Value from value stored in *this
		return (reinterpret_cast<_Ty *>(
			_Atomic_address::fetch_sub(_Value * sizeof (_Ty), _Order)));
		}

	_Ty *fetch_sub(ptrdiff_t _Value, memory_order _Order = memory_order_seq_cst) noexcept
		{	// subtract _Value from value stored in *this
		return (reinterpret_cast<_Ty *>(
			_Atomic_address::fetch_sub(_Value * sizeof (_Ty), _Order)));
		}

	_Ty *operator++(int) volatile noexcept
		{	// increment stored pointer
		return (fetch_add(1));
		}

	_Ty *operator++(int) noexcept
		{	// increment stored pointer
		return (fetch_add(1));
		}

	_Ty *operator--(int) volatile noexcept
		{	// decrement stored pointer
		return (fetch_sub(1));
		}

	_Ty *operator--(int) noexcept
		{	// decrement stored pointer
		return (fetch_sub(1));
		}

	_Ty *operator+=(ptrdiff_t _Right) volatile noexcept
		{	// add _Right to value stored in *this
		return (fetch_add(_Right) + _Right);
		}

	_Ty *operator+=(ptrdiff_t _Right) noexcept
		{	// add _Right to value stored in *this
		return (fetch_add(_Right) + _Right);
		}

	_Ty *operator-=(ptrdiff_t _Right) volatile noexcept
		{	// subtract _Right from value stored in *this
		return (fetch_sub(_Right) - _Right);
		}

	_Ty *operator-=(ptrdiff_t _Right) noexcept
		{	// subtract _Right from value stored in *this
		return (fetch_sub(_Right) - _Right);
		}

	_Ty *operator++() volatile noexcept
		{	// increment stored pointer
		return (*this += 1);
		}

	_Ty *operator++() noexcept
		{	// increment stored pointer
		return (*this += 1);
		}

	_Ty *operator--() volatile noexcept
		{	// decrement stored pointer
		return (*this -= 1);
		}

	_Ty *operator--() noexcept
		{	// decrement stored pointer
		return (*this -= 1);
		}
	};

具体调用的相关函数,可以继续在相关头文件里查看,这个其实没啥,代码还是比较清晰简单的。里面需要说明一下的是compare_exchange_strong(CAS),这也是其它语言实现无锁编程的方式,基本都类似。

四、实例

看一下自定义的智能指针的一个实例及相关应用的例程(cppreference.com):

 #include <thread>
#include <vector>
#include <iostream>
#include <atomic>
 
std::atomic<bool> lock(false); // holds true when locked
                               // holds false when unlocked
 
void f(int n)
{
    for (int cnt = 0; cnt < 100; ++cnt) {
        while(std::atomic_exchange_explicit(&lock, true, std::memory_order_acquire))
             ; // spin until acquired
        std::cout << "Output from thread " << n << '\n';
        std::atomic_store_explicit(&lock, false, std::memory_order_release);
    }
}
int main()
{
    std::vector<std::thread> v;
    for (int n = 0; n < 10; ++n) {
        v.emplace_back(f, n);
    }
    for (auto& t : v) {
        t.join();
    }
}

输出结果:

 Output from thread 2
Output from thread 6
Output from thread 7
...<exactly 1000 lines>...

在cppreference.com上相关的例程主要在相关的应用函数使用上,如std::atomic_flag_test_and_set, std::atomic_flag_test_and_set_explicit,包括std::memory_order,而直接的介绍上则很少有例程,这个简单说明一下。

五、总结

一般来说,++i和i++都不认为是原子操作,特别是在多核计算机普遍流行的今天,更是如此。原子操作是如此之美,是不是可以把锁替代掉呢?不要想得太简单,这只是给你提供了一个更多的选择的机会,而不是要让你把哪个干掉。不过总体仍然是表明,只要朝着简单易用的方向前进的东西,基本都是正确的。
大繁至简,亦如是!
在这里插入图片描述

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值