std::this_thread::sleep_for() 函数,在各次查验之间短期休眠。
std::condition_variable
std::condition_variable 仅限于与 std::mutex 一起使用std::condition_variable_any
std::condition_variable 应予优先采用,除非有必要令程序更灵活内
std::future<>)std::shared_future<>)std::future 和 std::shared_future。std::experimental 名字空间中给出了上述类模板的扩展版本:std::experimental::future<> 和 std::experimental::shared_future<>。
std::thread 没有提供直接回传结果的方法,所以函数模板 std::async() 应运而生
中。std::future 取得异步任务的函数返回值int find_the_answer_to_ltuae();
void do_other_stuff();
....
std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
do_other_stuff();
std::cout<<"The answer is "<<the_answer.get()<<std::endl;
std::async() 补充一个参数,以指定采用哪种运行方式, 参数的类型是 std::launch。
std::launch::deferred
wait() 或 get() ,任务函数才会执行std::launch::async
std::launch::deferred | std::launch:: async
auto f=std::async(std::launch::deferred,func,std::ref(parame));std::packaged_task<> 连结了 future 对象与函数(或可调用对象)
std::packaged_task<> 对象在执行任务时,会调用关联的函数(或可调用对象),把返回值保存为 future 的内部数据,并令 future 准备就绪std::packaged_task<> 实例之中,再传递给任务调度器或线程池std::packaged_task<> 具有成员函数 get_future(),它返回 std::future<> 实例std::packaged_task<> 特化示例:template<>
class packaged_task<std::string(std::vector<char>*,int)>
{
public:
template<typename Callable>
explicit packaged_task(Callable&& f);
std::future<std::string> get_future();
void operator()(std::vector<char>*,int);
};
std::packaged_task 对象是可调用对象
std::thread 对象std::packaged_task 实现std::promise 给出了一种异步求值的方法(类型为T)
std::future 对象与结果关联,能延后读出需要求取的值std::promise 和 std::future 可实现下面的工作机制
get_future()set_value()若经由 std::async() 调用的函数抛出异常,则会被保存到 future 中
get() 被调用get() 后存储在内的异常即被重新抛出std::promise 也具有同样的功能它通过成员函数的显式调用实现。
set_value()set_exception()extern std::promise<double> some_promise;
try
{
some_promise.set_value(calculate_value());
}
catch(...)
{
//std::current_exception() 用于捕获抛出的异常
some_promise.set_exception(std::current_exception());
}
some_promise.set_exception(std::make_exception_ptr(std::logic_error("foo ")));另一种方法保存异常到 future 中:
set() 成员函数,也不执行包装的任务,而直接销毁与 future 关联的 std::promise 对象或 std::packaged_task 对象std::future_error 存储为异步任务的状态数据std::future_errc::broken_promisefuture自身存在限制
std::shared_future。std::future 特性
get() 仅能被有效调用唯一一次get() 会进行移动操作,之后该值不复存在只要同步操作是一对一地在线程间传递数据,std::future 就都能处理
std::shared_future 对象的副本,它们为各线程独自所有,并被视作局部变量valid(),用于判别异步状态是否有效。std::shared_future,实现并行处理。每个单元格的终值都唯一确定,并可以用于其他单元格的计算公式std::shared_futurestd::promise<std::string> p;
std::shared_future<std::string> sf(p.get_future());
std::condition_variable 含有成员函数 wait_for() 和 wait_until()std::chrono::system_clock::now() 可返回系统时钟的当前时刻time_point 成员类型(member type),它是该时钟类自有的时间点类some_clock::now() 的返回值的类型就是 some_clock::time_pointstd::ratio<1,25>std::ratio<5,2>is_steady
std::chrono::system_clock 类不是恒稳时钟,因为它可调整。now(),后来返回的时间值甚至早于前一个std::chrono::steady_clockstd::chrono::system_clock
from_time_t() 和 to_time_t()std::chrono::high_resolution_clockstd::chrono::duration<> 是标准库中最简单的时间部件
std::chrono::duration>
std::chrono::duration>
std::chrono::duration> using namespace std::chrono_literals;
auto one_day=24h;
auto half_an_hour=30min;
auto max_time_between_messages=30ms;
std::chrono::duration_cast<> 完成std::chrono::duration<> 实例//等待某个future进入就绪状态,并以35毫秒为限
std::future<int> f=std::async(some_task);
if(f.wait_for(std::chrono::milliseconds(35))==std::future_status::ready)
do_something_with(f.get());
std::future_status::timeoutstd::future_status::readystd::future_status::deferredstd::chrono::time_point<> 的实例表示
time_since_epoch()
std::chrono::high_resolution_clock::now() + std::chrono::nanoseconds(500)
auto start=std::chrono::high_resolution_clock::now();
do_something();
auto stop=std::chrono::high_resolution_clock::now();
std::cout<<"do_something() took "
<(stop-start).count()
<<" seconds"< std::chrono::system_clock::to_time_point() 转换 time_t 值
some_clock::now()和前向偏移相加得出时间点std::condition_variable cv;
bool done;
std::mutex m;
bool wait_loop()
{
auto const timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(500);
std::unique_lock<std::mutex> lk(m);
while (!done)
{
if (cv.wait_until(lk, timeout) == std::cv_status::timeout)
break;
}
return done;
}
超时时限的最简单用途是,推迟特定线程的处理过程,若它无所事事,就不会占用其他线程的处理时间
std::this_thread::sleep_for()
std::this_thread::sleep_until()
普通的 std::mutex 和 std::recursive_mutex 不能限时加锁
std::timed_mutex 和 std::recursive_timed_mutex 可以限时加锁
try_lock_for() 和 try_lock_until()
C++ 标准库中接受超时时限的函数
std::this_thread
sleep_forsleep_unitlstd::condition_variable 或 std::condition_variable_any
wait_for(lock, duration)wait_until(lock, time_point)std::cv_status::timeout 或 std::cv_status::no_timeout
wait_for(lock, duration, predicate)wait_until(lock, time_point, predicate)std::timed_mutex, std::recursive_timed_mutex 或 std::shared_timed_mutex
try_lock_for(duration)try_lock_until(time_point)std::shared_timed_mutex
try_lock_shared_for(duration)try_lock_shared_until(time_point)std::unique_lock, unique_lock(lockable, duration) 或 unique_lock(lockable, time_point)
try_lock_for(duration)try_lock_until(time_point)owns_lock() 返回 true,否则返回 falsestd::shared_lock, shared_lock(lockable,duration) 或 shared_lock(lockable,time_point)
try_lock_for(duration)try_lock_until(time_point)owns_lock() 返回 true,否则返回 falsestd::future 或 std::shared_future
wait_for(duration)wait_until(time_point)std::future_status::timeoutstd::future_status::readystd::future_status::deferredstd::async())
std::async() 每次都开启新线程,那么只要递归3层,就会有8个线程同时运行spawn_task())
std::result_of 被弃用,这个函数需要重写std::async() 退下“火线”,而优先采用线程池模式std::promise 和 std:: packaged_task 的新版本
std::experimental::future 实例,而非 std::futurestd::experimental::future<int> find_the_answer;
auto fut=find_the_answer();
auto fut2=fut.then(find_the_question);
assert(!fut.valid());
assert(fut2.valid());
find_the_question() 会在“某一线程上”运行
when_any() 胜任如下情形
std::experimental::when_any() 和 std::experimental::when_all()的全部使用方式:
std::experimental::future移动复制到函数中,而且它们都以传值的方式接收参数,所以我们需要显式地向函数内部移动 future,或传递临时变量。std::experimental::latch 由头文件 定
count_down(),一旦计数器减到 0,它就进入就绪状态wait()is_ready()count_down_and_wait()void foo(){
unsigned const thread_count=...;
//构建线程闩对象done
latch done(thread_count);
my_data data[thread_count];
std::vector<std::future<void> > threads;
for(unsigned i=0;i<thread_count;++i)
//用std::async()发起相同数量的线程
threads.push_back(std::async(std::launch::async,[&,i]{
//各线程负责生成相关的数据块
data[i]=make_data(i);
//在完成时即令计数器减持
done.count_down();
//进行下一步处理
do_more_stuff();
}));
//主线程在处理生成的数据之前,只要在线程闩上等待,就能等到全部数据准备完成
done.wait();
process_data(data,thread_count);
//std::future 的析构函数会发生自动调用,这保证了前面所启动的线程全都会结束运行
}
std::experimental::barrierstd::experimental::flex_barrier 中定义std::experimental::barrier 针对的场景
arrive_and_wait() 等待同步组的其他线程arrive_and_drop(),即可令线程显式地脱离其同步组std::experimental::flex_barrier 类的接口与 std::experimental::barrier 类的不同之处仅仅在于
内is_lock_free() ,准许使用者判定某一给定类型上的操作是能由原子指令(atomic instruction)直接实现
x.is_lock_free() 返回 truex.is_lock_free() 返回 falsestd::atomicstd::atomic::is_always_lock_free 的值在编译期即确定为 falsestd::atomic 都以无锁结构形式实现std::atomic::is_always_lock_free 的值会是 trueis_lock_free() 成员函数
std::atomic_flagstd::atomic_flag 的对象在初始化时清零test_and_set() 查值并设置成立clear() 清零std::atomic<> 特化得出的std::atomic<> 特化, 而不用别名load()store()exchange()compare_exchange_weak()compare_exchange_strong()+=、−=、*= 和 |=std::atomic<> 特化都支持 ++ 和 −− 运算
fetch_add() 和 fetch_or() 等std::atomic<> 并不局限于上述特化类型, 它其实具有泛化模板
load()、store()
exchange()、compare_exchange_weak()、compare_exchange_strong()std::memory_order 取值
std::memory_order 具有6个可能的值
std::memory_order_relaxed std::memory_order_releasestd::memory_order_seq_cststd::memory_order_relaxedstd::memory_order_consumestd::memory_order_acquirestd::memory_order_seq_csstd::memory_order_relaxedstd::memory_order_consumestd::memory_order_acquirestd::memory_order_releasestd::memory_order_acq_relstd::memory_order_seq_cststd::atomic_flag 是最简单的标准原子类型,表示一个布尔标志。
std::atomic_flag 类型的对象必须由宏 ATOMIC_FLAG_INIT 初始化
std::atomic_flag f=ATOMIC_FLAG_INIT。std::atomic_flag
std::atomic_flag 对象具有静态存储期,它就会保证以静态方式初始化,从而避免初始化次序的问题。
std::atomic_flag 对象的初始化后,我们只能执行3种操作
clear()test_and_set()clear() 和 test_and_set() 指定内存次序clear()是存储操作,因此无法采用 std::memory_order_acquire 或std::memory_order_acq_rel内存次序test_and_set() 是“读-改-写”操作,因此能采用任何内存次序//调用显式地采用释放语义将标志清零
f.clear(std::memory_order_release);
//调用采用默认的内存次序,获取旧值并设置标志成立
bool x=f.test_and_set();
std::atomic_flag 对象拷贝构造出另一个对象std::atomic_flag 功能有限,所以它可以完美扩展成自旋锁互斥(spin-lock mutex)class spinlock_mutex
{
std::atomic_flag flag;
public:
spinlock_mutex():
flag(ATOMIC_FLAG_INIT)
{}
void lock()
{
while(flag.test_and_set(std::memory_order_acquire));
}
void unlock()
{
flag.clear(std::memory_order_release);
}
};
lock() 函数内忙等。
std::atomic_flag,它是一个功能更齐全的布尔标志store()是存储操作load()是载入操作exchange() 是“读-改-写” 操作std::atomic<bool> b;
bool x=b.load(std::memory_order_acquire);
b.store(true);
x=b.exchange(false,std::memory_order_acq_rel);
compare_exchange_weak() 和 compare_exchange_strong()bool expected=false;
extern atomic<bool> b; //由其他源文件的代码设定变量的值
//只要expected变量还是false,
//就说明compare_exchange_weak()的调用发生佯败,我们就继续循环
while(!b.compare_exchange_weak(expected,true) && !expected);
compare_exchange_strong() 才返回 falsecompare_exchange_weak() 可能导致佯败,但改用 compare_exchange_strong() 却会形成双重嵌套循环(因 compare_exchange_strong()自身内部含有一个循环),那么采用compare_exchange_weak() 比较有利于性能。compare_exchange_strong() 则更加合理。因为只要预期值没有变化,就可避免重复计算std::memory_order_acq_rel 内存次序std::memory_order_relaxed 内存次序std::atomic 和 std::atomic_flag 的另一个不同点是
is_lock_free(),检查 std::atomic 是否具备真正的无锁操作std::atomic 相似,同样不能拷贝复制或拷贝赋值std::atomic 也具备成员函数
is_lock_free()load()store()exchange()compare_exchange_weak()compare_exchange_strong()T* 类型std::atomic 提供的新操作是算术形式的指针运算
fetch_add()
fetch_sub()
+=−=++−−std::atomic ,其指向 Foo 对象数组中的第一项,那么操作 x+=3 会使之变为指向数组第四项,并返回 Foo* 型的普通指针class Foo{};
Foo some_array[5];
std::atomic<Foo*> p(some_array);
//接收附加的参数
//p.fetch_add(3,std::memory_order_release);
Foo* x=p.fetch_add(2); //令p加2,返回旧值
assert(x==some_array);
assert(p.load()==&some_array[2]);
x=(p-=1); // 令p减1,返回新值
assert(x==&some_array[1]);
assert(p.load()==&some_array[1]);
std::atomic 和 std::atomic 这样的整数原子类型上,可以执行的操作颇为齐全
load()store()exchange()compare_exchange_weak()compare_exchange_strong()fetch_add()fetch_sub()fetch_and()fetch_or()fetch_xor()+=、−=、&=、|= 和 ^=++x 、x++ 、−−x 和 x−−std::atomic<>
memcpy() 或采取与之等效的行为完成它std::atomic<> 生成无锁代码
memcpy() 和 memcmp() 的适用条件
std::atomic 和 std::atomic 可以为我们所用
compare_exchange_strong()函数,其行为可能出人意料int 或 void* 的体积,只要用户自定义的 UDT 类型的体积不超过它们,那么在大多数常见的硬件平台上,都可以为 std::atomic 类型采用原子指令std::atomic> vector含有非平实拷贝构造函数和非平实拷贝赋值操作符std::atomic_load()std::atomic_flag 的非成员函数是
std::atomic_flag_test_and_set()std::atomic_flag_clear()std::atomic_flag_test_and_set_explicit()std::atomic_flag_clear_explicit()std::shared_ptr<my_data> p;
void process_global_data()
{
std::shared_ptr<my_data> local=std::atomic_load(&p);
process_data(local);
}
void update_global_data()
{
std::shared_ptr<my_data> local(new my_data);
std::atomic_store(&p,local);
}
std::share_ptr 的函数也具有变体std::experimental::atomic_shared_ptr,其也是一种原子类型。我们须包含头文件 才能使用该类型
atomic_shared_ptr<> 被设计成一个独立类型,
std::shared_ptr 对象,它没有增加额外开销is_lock_free() 判定std::share_ptr类型
std::experimental::atomic_shared_ptr 应予优先采用(就算它不是无锁实现)
std::mutex 和 std::future<>,都以这种强制服从的内存次序为基础data_ready 的操作提供了所需的强制次序,它属于 std::atomic 类型#include
#include
#include
std::vector<int> data;
std::atomic<bool> data_ready(false);
void reader_thread()
{
while(!data_ready.load())
{
std::this_thread::sleep(std::chrono::milliseconds(1));
}
std::cout<<"The answer="<<data[0]<<"\n";
}
void writer_thread()
{
data.push_back(42);
data_ready=true;
}
memory_order_consumememory_order_consume 标记,因此实际上这一区别对我们影响甚微。fetch_add() 和 exchange()