C++11 并发指南六(atomic 类型详解一 atomic_flag 介绍) 一文介绍了 C++11 中最简单的原子类型 std::atomic_flag,但是 std::atomic_flag 过于简单,只提供了 test_and_set 和 clear 两个 API,不能满足其他需求(如 store, load, exchange, compare_exchange 等),因此本文将介绍功能更加完善的 std::atomic 类。
std::atomic 是模板类,一个模板类型为 T 的原子对象中封装了一个类型为 T 的值。
template <class T> struct atomic;
原子类型对象的主要特点就是从不同线程访问不会导致数据竞争(data race)。因此从不同线程访问某个原子对象是良性 (well-defined) 行为,而通常对于非原子类型而言,并发访问某个对象(如果不做任何同步操作)会导致未定义 (undifined) 行为发生。
C++11 标准中的基本 std::atomic 模板定义如下:
template < class T > struct atomic { bool is_lock_free() const volatile; bool is_lock_free() const; void store(T, memory_order = memory_order_seq_cst) volatile; void store(T, memory_order = memory_order_seq_cst); T load(memory_order = memory_order_seq_cst) const volatile; T load(memory_order = memory_order_seq_cst) const; operator T() const volatile; operator T() const; T exchange(T, memory_order = memory_order_seq_cst) volatile; T exchange(T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T &, T, memory_order, memory_order) volatile; bool compare_exchange_weak(T &, T, memory_order, memory_order); bool compare_exchange_strong(T &, T, memory_order, memory_order) volatile; bool compare_exchange_strong(T &, T, memory_order, memory_order); bool compare_exchange_weak(T &, T, memory_order = memory_order_seq_cst) volatile; bool compare_exchange_weak(T &, T, memory_order = memory_order_seq_cst); bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst) volatile; bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst); atomic() = default; constexpr atomic(T); atomic(const atomic &) = delete; atomic & operator=(const atomic &) = delete; atomic & operator=(const atomic &) volatile = delete; T operator=(T) volatile; T operator=(T); };
另外,C++11 标准库 std::atomic 提供了针对整形(integral)和指针类型的特化实现,分别定义如下:
针对整形(integal)的特化,其中 integal 代表了如下类型char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, char16_t, char32_t, wchar_t:
template
<>
struct
atomic<integral> {
bool
is_lock_free()
const
volatile
;
bool
is_lock_free()
const
;
void
store(integral, memory_order = memory_order_seq_cst)
volatile
;
void
store(integral, memory_order = memory_order_seq_cst);
integral load(memory_order = memory_order_seq_cst)
const
volatile
;
integral load(memory_order = memory_order_seq_cst)
const
;
operator integral()
const
volatile
;
operator integral()
const
;
integral exchange(integral, memory_order = memory_order_seq_cst)
volatile
;
integral exchange(integral, memory_order = memory_order_seq_cst);
bool
compare_exchange_weak(integral&, integral, memory_order, memory_order)
volatile
;
bool
compare_exchange_weak(integral&, integral, memory_order, memory_order);
bool
compare_exchange_strong(integral&, integral, memory_order, memory_order)
volatile
;
bool
compare_exchange_strong(integral&, integral, memory_order, memory_order);
bool
compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst)
volatile
;
bool
compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst);
bool
compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst)
volatile
;
bool
compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst);
integral fetch_add(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_add(integral, memory_order = memory_order_seq_cst);
integral fetch_sub(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_sub(integral, memory_order = memory_order_seq_cst);
integral fetch_and(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_and(integral, memory_order = memory_order_seq_cst);
integral fetch_or(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_or(integral, memory_order = memory_order_seq_cst);
integral fetch_xor(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_xor(integral, memory_order = memory_order_seq_cst);
atomic() =
default
;
constexpr
atomic(integral);
atomic(
const
atomic&) =
delete
;
atomic& operator=(
const
atomic&) =
delete
;
atomic& operator=(
const
atomic&)
volatile
=
delete
;
integral operator=(integral)
volatile
;
integral operator=(integral);
integral operator++(
int
)
volatile
;
integral operator++(
int
);
integral operator--(
int
)
volatile
;
integral operator--(
int
);
integral operator++()
volatile
;
integral operator++();
integral operator--()
volatile
;
integral operator--();
integral operator+=(integral)
volatile
;
integral operator+=(integral);
integral operator-=(integral)
volatile
;
integral operator-=(integral);
integral operator&=(integral)
volatile
;
integral operator&=(integral);
integral operator|=(integral)
volatile
;
integral operator|=(integral);
integral operator^=(integral)
volatile
;
integral operator^=(integral);
};
|
针对指针的特化:
template
<
class
T>
struct
atomic<T*> {
bool
is_lock_free()
const
volatile
;
bool
is_lock_free()
const
;
void
store(T*, memory_order = memory_order_seq_cst)
volatile
;
void
store(T*, memory_order = memory_order_seq_cst);
T* load(memory_order = memory_order_seq_cst)
const
volatile
;
T* load(memory_order = memory_order_seq_cst)
const
;
operator T*()
const
volatile
;
operator T*()
const
;
T* exchange(T*, memory_order = memory_order_seq_cst)
volatile
;
T* exchange(T*, memory_order = memory_order_seq_cst);
bool
compare_exchange_weak(T*&, T*, memory_order, memory_order)
volatile
;
bool
compare_exchange_weak(T*&, T*, memory_order, memory_order);
bool
compare_exchange_strong(T*&, T*, memory_order, memory_order)
volatile
;
bool
compare_exchange_strong(T*&, T*, memory_order, memory_order);
bool
compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst)
volatile
;
bool
compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst);
bool
compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst)
volatile
;
bool
compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst);
T* fetch_add(
ptrdiff_t
, memory_order = memory_order_seq_cst)
volatile
;
T* fetch_add(
ptrdiff_t
, memory_order = memory_order_seq_cst);
T* fetch_sub(
ptrdiff_t
, memory_order = memory_order_seq_cst)
volatile
;
T* fetch_sub(
ptrdiff_t
, memory_order = memory_order_seq_cst);
atomic() =
default
;
constexpr
atomic(T*);
atomic(
const
atomic&) =
delete
;
atomic& operator=(
const
atomic&) =
delete
;
atomic& operator=(
const
atomic&)
volatile
=
delete
;
T* operator=(T*)
volatile
;
T* operator=(T*);
T* operator++(
int
)
volatile
;
T* operator++(
int
);
T* operator--(
int
)
volatile
;
T* operator--(
int
);
T* operator++()
volatile
;
T* operator++();
T* operator--()
volatile
;
T* operator--();
T* operator+=(
ptrdiff_t
)
volatile
;
T* operator+=(
ptrdiff_t
);
T* operator-=(
ptrdiff_t
)
volatile
;
T* operator-=(
ptrdiff_t
);
};
|
好了,对 std::atomic 有了一个最基本认识之后我们来看 std::atomic 的成员函数吧。
std::atomic 的构造函数如下:
default (1) | atomic() noexcept = default; |
---|---|
initialization (2) | constexpr atomic (T val) noexcept; |
copy [deleted] (3) | atomic (const atomic&) = delete; |
请看下例:
#include <iostream> // std::cout
#include <atomic> // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT
#include <thread> // std::thread, std::this_thread::yield
#include <vector> // std::vector
// 由 false 初始化一个 std::atomic<bool> 类型的原子变量
std::atomic<
bool
> ready(
false
);
std::atomic_flag winner = ATOMIC_FLAG_INIT;
void
do_count1m(
int
id)
{
while
(!ready) { std::this_thread::yield(); }
// 等待 ready 变为 true.
for
(
volatile
int
i=0; i<1000000; ++i) {}
// 计数
if
(!winner.test_and_set()) {
std::cout <<
"thread #"
<< id <<
" won!\n"
;
}
}
int
main ()
{
std::vector<std::
thread
> threads;
std::cout <<
"spawning 10 threads that count to 1 million...\n"
;
for
(
int
i=1; i<=10; ++i) threads.push_back(std::
thread
(count1m,i));
ready =
true
;
for
(
auto
& th : threads) th.join();
return
0;
}
|
std::atomic 的赋值操作函数定义如下:
set value (1) | T operator= (T val) noexcept; T operator= (T val) volatile noexcept; |
---|---|
copy [deleted] (2) | atomic& operator= (const atomic&) = delete; atomic& operator= (const atomic&) volatile = delete; |
可以看出,普通的赋值拷贝操作已经被禁用。但是一个类型为 T 的变量可以赋值给相应的原子类型变量(相当与隐式转换),该操作是原子的,内存序(Memory Order) 默认为顺序一致性(std::memory_order_seq_cst),如果需要指定其他的内存序,需使用 std::atomic::store()。
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread, std::this_thread::yield
std::atomic <
int
> foo = 0;
void
set_foo(
int
x)
{
foo = x;
// 调用 std::atomic::operator=().
}
void
print_foo()
{
while
(foo == 0) {
// wait while foo == 0
std::this_thread::yield();
}
std::cout <<
"foo: "
<< foo <<
'\n'
;
}
int
main()
{
std::
thread
first(print_foo);
std::
thread
second(set_foo, 10);
first.join();
second.join();
return
0;
}
|
本节主要介绍基本 std::atomic 类型所具备的操作(即成员函数)。我们知道 std::atomic 是模板类,一个模板类型为 T 的原子对象中封装了一个类型为 T 的值。本文<std::atomic 基本介绍>一节中也提到了 std::atomic 类模板除了基本类型以外,还针对整形和指针类型做了特化。 特化的 std::atomic 类型支持更多的操作,如 fetch_add, fetch_sub, fetch_and 等。本小节介绍基本 std::atomic 类型所具备的操作:
bool
is_lock_free()
const
volatile
noexcept
;
bool
is_lock_free()
const
noexcept
;
|
void
store (T val, memory_order sync = memory_order_seq_cst)
volatile
noexcept
;
void
store (T val, memory_order sync = memory_order_seq_cst)
noexcept
;
|
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_release | Release |
memory_order_seq_cst | Sequentially consistent |
#include <iostream> // std::cout
#include <atomic> // std::atomic, std::memory_order_relaxed
#include <thread> // std::thread
std::atomic<
int
> foo(0);
// 全局的原子对象 foo
void
set_foo(
int
x)
{
foo.store(x, std::memory_order_relaxed);
// 设置(store) 原子对象 foo 的值
}
void
print_foo()
{
int
x;
do
{
x = foo.load(std::memory_order_relaxed);
// 读取(load) 原子对象 foo 的值
}
while
(x == 0);
std::cout <<
"foo: "
<< x <<
'\n'
;
}
int
main ()
{
std::
thread
first(print_foo);
// 线程 first 打印 foo 的值
std::
thread
second(set_foo, 10);
// 线程 second 设置 foo 的值
first.join();
second.join();
return
0;
}
|
T load (memory_order sync = memory_order_seq_cst)
const
volatile
noexcept
;
T load (memory_order sync = memory_order_seq_cst)
const
noexcept
;
|
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_seq_cst | Sequentially consistent |
#include <iostream> // std::cout
#include <atomic> // std::atomic, std::memory_order_relaxed
#include <thread> // std::thread
std::atomic<
int
> foo(0);
// 全局的原子对象 foo
void
set_foo(
int
x)
{
foo.store(x, std::memory_order_relaxed);
// 设置(store) 原子对象 foo 的值
}
void
print_foo()
{
int
x;
do
{
x = foo.load(std::memory_order_relaxed);
// 读取(load) 原子对象 foo 的值
}
while
(x == 0);
std::cout <<
"foo: "
<< x <<
'\n'
;
}
int
main ()
{
std::
thread
first(print_foo);
// 线程 first 打印 foo 的值
std::
thread
second(set_foo, 10);
// 线程 second 设置 foo 的值
first.join();
second.join();
return
0;
}
|
operator T()
const
volatile
noexcept
;
operator T()
const
noexcept
;
|
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread, std::this_thread::yield
std::atomic<
int
> foo = 0;
std::atomic<
int
> bar = 0;
void
set_foo(
int
x)
{
foo = x;
}
void
copy_foo_to_bar()
{
// 如果 foo == 0,则该线程 yield,
// 在 foo == 0 时, 实际也是隐含了类型转换操作,
// 因此也包含了 operator T() const 的调用.
while
(foo == 0) std::this_thread::yield();
// 实际调用了 operator T() const, 将foo 强制转换成 int 类型,
// 然后调用 operator=().
bar =
static_cast
<
int
>(foo);
}
void
print_bar()
{
// 如果 bar == 0,则该线程 yield,
// 在 bar == 0 时, 实际也是隐含了类型转换操作,
// 因此也包含了 operator T() const 的调用.
while
(bar == 0) std::this_thread::yield();
std::cout <<
"bar: "
<< bar <<
'\n'
;
}
int
main ()
{
std::
thread
first(print_bar);
std::
thread
second(set_foo, 10);
std::
thread
third(copy_foo_to_bar);
first.join();
second.join();
third.join();
return
0;
}
|
T exchange (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; T exchange (T val, memory_order sync = memory_order_seq_cst) noexcept;
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
请看下面例子,各个线程计数至 1M,首先完成计数任务的线程打印自己的 ID,
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread
#include <vector> // std::vector
std::atomic<
bool
> ready(
false
);
std::atomic<
bool
> winner(
false
);
void
count1m (
int
id)
{
while
(!ready) {}
// wait for the ready signal
for
(
int
i = 0; i < 1000000; ++i) {}
// go!, count to 1 million
if
(!winner.exchange(
true
)) { std::cout <<
"thread #"
<< id <<
" won!\n"
; }
};
int
main ()
{
std::vector<std::
thread
> threads;
std::cout <<
"spawning 10 threads that count to 1 million...\n"
;
for
(
int
i = 1; i <= 10; ++i) threads.push_back(std::
thread
(count1m,i));
ready =
true
;
for
(
auto
& th : threads) th.join();
return
0;
}
|
(1) | bool compare_exchange_weak (T& expected, T val, memory_order sync = memory_order_seq_cst) volatile noexcept; bool compare_exchange_weak (T& expected, T val, memory_order sync = memory_order_seq_cst) noexcept; |
---|---|
(2) | bool compare_exchange_weak (T& expected, T val, memory_order success, memory_order failure) volatile noexcept; bool compare_exchange_weak (T& expected, T val, memory_order success, memory_order failure) noexcept; |
expected 的值不会改变。
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread
#include <vector> // std::vector
// a simple global linked list:
struct
Node {
int
value; Node* next; };
std::atomic<Node*> list_head(
nullptr
);
void
append(
int
val)
{
// append an element to the list
Node* newNode =
new
Node{val, list_head};
// next is the same as: list_head = newNode, but in a thread-safe way:
while
(!list_head.compare_exchange_weak(newNode->next,newNode)) {}
// (with newNode->next updated accordingly if some other thread just appended another node)
}
int
main ()
{
// spawn 10 threads to fill the linked list:
std::vector<std::
thread
> threads;
for
(
int
i = 0; i < 10; ++i) threads.push_back(std::
thread
(append, i));
for
(
auto
& th : threads) th.join();
// print contents:
for
(Node* it = list_head; it!=
nullptr
; it=it->next)
std::cout <<
' '
<< it->value;
std::cout <<
'\n'
;
// cleanup:
Node* it;
while
(it=list_head) {list_head=it->next;
delete
it;}
return
0;
}
|
9 8 7 6 5 4 3 2 1 0
|
(1) | bool compare_exchange_strong (T& expected, T val, memory_order sync = memory_order_seq_cst) volatile noexcept; bool compare_exchange_strong (T& expected, T val, memory_order sync = memory_order_seq_cst) noexcept; |
---|---|
(2) | bool compare_exchange_strong (T& expected, T val, memory_order success, memory_order failure) volatile noexcept; bool compare_exchange_strong (T& expected, T val, memory_order success, memory_order failure) noexcept; |
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread
#include <vector> // std::vector
// a simple global linked list:
struct
Node {
int
value; Node* next; };
std::atomic<Node*> list_head(
nullptr
);
void
append(
int
val)
{
// append an element to the list
Node* newNode =
new
Node{val, list_head};
// next is the same as: list_head = newNode, but in a thread-safe way:
while
(!(list_head.compare_exchange_strong(newNode->next, newNode)));
// (with newNode->next updated accordingly if some other thread just appended another node)
}
int
main ()
{
// spawn 10 threads to fill the linked list:
std::vector<std::
thread
> threads;
for
(
int
i = 0; i < 10; ++i) threads.push_back(std::
thread
(append, i));
for
(
auto
& th : threads) th.join();
// print contents:
for
(Node* it = list_head; it!=
nullptr
; it=it->next)
std::cout <<
' '
<< it->value;
std::cout <<
'\n'
;
// cleanup:
Node* it;
while
(it=list_head) {list_head=it->next;
delete
it;}
return
0;
}
|
好了,本文花了大量的篇幅介绍 std::atomic 基本类型,下一篇博客我会给大家介绍 C++11 的标准库中std::atomic 针对整形(integral)和指针类型的特化版本做了哪些改进。
一、router-linkURL路径:http://localhost:8081/#/test?userid=1&amp;amp;amp;lt;router-link :to=&amp;amp;quot;{path:'/test',query: {userid: id}}&amp;amp;quot;&amp;amp;amp;gt;跳转&amp;amp;amp;lt;/router-link&amp;amp;amp;gt;收:var id = t
分面通常使用绘图方法+①facet_wrap(~varible)/facet_wrap(formula) 较适用于单个变量②facet_grid(vertical ~ horizion)/facet_grid(formula) 较适用于多个变量详细讲解可参考 http://www.cookbook-r.com/Graphs/Facets_(ggplot2)/其他图形调整1、转换数据### Tr...
最近使用rasa时,安装Mitie。根据CSDN博主「无名之辈FTER」的原创文章原文链接:https://blog.csdn.net/AndrExpert/article/details/104328946离线安装的方式首先,下载MITIE源码GitHub - mit-nlp/MITIE: MITIE: library and tools for information extraction和中文词向量模型total_word_feature_extractor_zh.dat;其次,安装V
手动启动Remote Desktop Services服务,报错提示:“系统无法找到指定文件”解决方法:1、打开regedit,找到路径HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\TermService\Parameters,发现少了一个二制文件serviceDLL。2、手动创建,右键新建-->可扩充字符段值,设置名称...
Spring、SpringMVC、Mybatics整合导包、配置0、目录:1、导包:Spring【总整合开发包】:提取码:ieqo【ioc核心】:提取码:7ijmcommons-logging-1.1.3.jar spring-aop-4.0.0.RELEASE.jar spring-beans-4.0.0.RELEASE.jar spring-context-4.0.0.RELEASE.jar spring-core-4.0.0.RELEASE.jar...
程序员是一个怎样的群体?近年来,随着程序员的崛起,关于他们的话题越来越多前阵子微博上有条新闻标题叫:年薪百万,却被亲妈拍卖。看到这标题,着实吓一跳,进入网页查看一番才清楚事情原委被拍卖的儿子是程序员,上海人,年薪百万,36岁还没有对象,这位大妈实在没招了,便在网上拍卖儿子,并承若结婚后退全款有人劝大妈:儿子晚点结婚没关系。大妈:哎呦,他好几个同事都已经秃顶了在线求儿子心里阴影...
socket通信方式 通常使用的是socket通信模式为c/s模式,就是通过服务端创建连接,并绑定监听相关的端口,客户端通过连接至相应的端口,实现使用tcp的三次握手来进行可靠性连接,从而达到数据传输。如果对应的客户端和服务端在同一台服务器上,因是数据内部通信方式,可直接相连接。但是当我们的客户端和服务端分别部署在不同的服务器上,有时候会出现客户端连接的时候提示connect r...
需求分析:过往实事---小中学写作文常需查找好词句来增料,然而每到用时方恨少询问同学以及邻家学生---都有过无米下炊的烦恼,新闻报道---前些年常有什么“马爱牛”等用典陈旧反复的弊病语文教育现状---过于偏重写作方法教学,且谈的多写的少,导致口若悬河提笔无字最后成品:专题网站,以网站网页的方式实时呈现,随时查找随时获取方法:网络广泛扒取,SVM算法甄选优势:从半自动走向自动-...
假设你的文件名是a.txt,写到b.txtd = {}for line in open('a.txt'): d[line] = d.get(line, 0) + 1 fd = open('b.txt', 'w')for k, v in d.items(): if v > 1: fd.write(k)fd.close()...
工作中,很多地方,都存在只知道手机号,同时也想知道这个手机号的归属地的情况。 本功能,即可实现这个要求。同时,可在此基础上做调整,实现其它功能,如制作一个属于自己的手机号段归属库等。。。 实现思路:通过网上提供的免费查询接口,取得返回信息,并对返回信息进行解析处理,获取自己需要的数据。 使用控件:LitJson.dll (LitJson.dll下载 或到网上搜索) 代...
InputManagerService启动简要Android系统启动Zygote -> SystemServer -> new SystemServer().run() -> startOtherServices(t) -> new InputManagerService(context);ANR InputDispatching TimeOut超时判断这篇也有相关InputReader\InputDispatcher重要线程启动图进入InputManagerServ
最近在搭spark集群的时候,成功启动集群,但是访问master的WorkerUI界面却看不到子节点,也就是worker id那里为空的,如图: 解决这个问题,关键是改spark的conf下面的spark-env.sh文件: 注意点就是,下面的masterid必须是ip,之前填master,能启动,但是界面看不到worker。 master配置:export JAVA_HOME=/o...