记录篇-c++11特性学习-新语法3之线程相关

前言

本文是记录个人对c++11新特性的学习,文中所用可能会采用其他大神的图、思路、例子等,请大神们见谅。本博客文章是在学习过程的一些总结,整理出来,分享给大家,希望对各位读者有帮助,文章中的总结可能存在很多不完整或有错误的地方,也希望读者指出。

1、std::thread

c++11中,可以通过创建std::thread类的对象来创建其他线程,每个std :: thread对象都可以与一个线程相关联,头文件< thread>
使用std :: thread对象附加一个回调。 这些回调可以为函数指针、函数对象、Lambda函数
通过std::thread thObj(< CALLBACK>)来创建

#include <iostream>
#include <thread>

void thread_function() {
    for (int i = 0; i < 5; i++)
        std::cout << "thread function excuting" << std::endl;
}

class DisplayThread {
public:
    void operator ()() {
        for (int i = 0; i < 100; i++)
            std::cout << "Display Thread Excecuting" << std::endl;
    }
};

int main() {
    std::thread threadObj(thread_function);   //调用函数指针
    std::thread threadObj((DisplayThread()));  //调用函数对象   
    for (int i = 0; i < 5; i++)
        std::cout << "Display from MainThread" << std::endl;
    threadObj.join();
    std::cout << "Exit of Main function" << std::endl;
    return 0;
}

std::thread::get_id() —成员函数中给出对应线程对象的id;
std::thread::hardware_concurrency()–获取CPU核心数量
std::this_thread::sleep_for(std::chrono::second(3))–让当前线程休眠一定时间,例如当前为休眠3秒
join():调用join()函数等待这个线程执行完毕
detach():调用detach()后,std::thread对象不再与实际执行线程相关联

1.1、线程创建方式

通过std::bind或者lambda表达式创建线程

void func(int a, double b)
{
}
int main()
{
	std::thread t1(std::bind(fun, 1, 2));
	std::thread t2([](int a, double b){}, 1, 2);
	t1.join();
	t2.join();

	std::thread t3(func, 1, 2); //这里直接创建线程而使用join或者detach,会导致func函数未执行完,std::thread生命周期就结束,引发异常。
	return 0;
}
std::vector<std::thread> g_lsit;
std::vector<std::shared_ptr<std::thread>> g_lsit2;
void createThread()
{
 	std::thread t(func);
 	g_list.push_back(std::move(t));
 	g_list2.push_back(std::make_shared<std::thread>(func));
}
int main()
{
	createThread();
	for(auto& thread : g_list)
	{
		thread.join();
	}
	for(auto& thread : g_list2)
	{
		thread->join();
	}	
	return 0;
}

注意点:
1)线程可以通过std::move函数进行移动,但不能复制,线程被移动后,线程对象不再代表任何线程;
2)std::thread出了作用域之后将会析构,如果线程函数还未执行完则会发生错误,所以 需要保证线程函数的生命周期在std::thread的生命周期内,通过join或者detach函数保证,或者将线程对象保存到容器中来保证

1.2、线程传递参数

要将参数传递给线程的可关联对象或函数,只需将参数传递给std::thread构造函数,默认情况下,所有的参数都将复制到新线程的内部存储中

#include <iostream>
#include <string>
#include <thread>

void threadCallback(int x, std::string str) {
  std::cout << "Passed Number = " << x << std::endl;
  std::cout << "Passed String = " << str << std::endl;
}

void threadCallback(int const& x) {
  int& y = const_cast<int&>(x);
  y++;
  std::cout << "Inside Thread x = " << x << std::endl;
}

class DummyClass {
 public:
  DummyClass() { }
  DummyClass(const DummyClass& obj) { }
  void sampleMemberfunction(int x) {
    std::cout << "Inside sampleMemberfunction " << x << std::endl;
  }
};

int main() {
  int x = 10;
  std::string str = "Sample String";
  std::thread threadObj(threadCallback, x, str);  //值传递参数方式
  std::thread threadObj(threadCallback, x);  //引用传递参数方式,引用方式传递到线程值是x的引用复制值,所以main内的x是不会因为threadCallback的内部参数改变而改变
  std::thread threadObj(threadCallback, std::ref(x));//使用std::ref可进行修改,threadCallback内部修改的X值,在main中也同样被修改。

  DummyClass dummyObj;
  int x = 10;
  std::thread threadObj(&DummyClass::sampleMemberfunction, &dummyObj, x);  //指定一个类的成员函数的指针作为线程函数,将指针传递给成员函数作为回调函数,并将指针指向对象作为第二个参数
 
  threadObj.join();
  return 0;
}

std::ref 用于包装按引用传递的值,函数内部的修改影响外面。
std::cref 用于包装按const引用传递的值,const引用传递,函数内部不能修改。

join和detach

join():等待线程完成函数,主线程需要等待子线程运行结束了才可以结束
detach():分离线程函数,使用detach()函数会让线程在后台运行;如果线程与主线程之间共享变量或者引用,则要慎用分离,防止出现主线程先结束,销毁线程对象及局部变量,引发未知错误
注:同一个线程不能同时调用jion()和detach()

joinable():判断当前线程是否可连接,返回true为可连接

线程join和detach都不调用

//std::thread的构造函数如下
class thread {
  private:
    id              _M_id;
  public:
    thread() noexcept = default;

    template<typename _Callable, 
              typename... _Args,
              typename = _Require<__not_same<_Callable>>>
    explicit thread(_Callable&& __f, _Args&&... __args) {
          //...
    }

    ~thread() {
      if (joinable())
        std::terminate();
    }
    // 禁止复制
    thread(const thread&) = delete;
    thread& operator=(const thread&) = delete;

    // std::thread 只具有移动属性
    thread(thread&& __t) noexcept
    { swap(__t); }

    thread& operator=(thread&& __t) noexcept {
      if (joinable())
          std::terminate();
      swap(__t);
      return *this;
    }
    //...
  }

从上可知:如果线程不调用join和detach,线程的状态将是joinable,在析构时直接调用terminate函数,程序将直接退出

线程内调用join

线程内调用自己jion自己,是直接报错误的,必须是其他线程调用join

2、互斥量

用于线程同步、保护多线程访问的共享数据
1)std::mutex–独占的互斥量,不能递归使用
2)std::timed_mutex–带超时的互斥量,不能递归使用
3)std::recursive_mutex–递归互斥量,不带超时功能
4)std::recursive_timed_mutex–带超时的递归互斥量

2.1、mutex和lock_guard

互斥量包装程序,它提供了一种方便的RAII(Resource acquisition is initialization )风格的机制来在作用域块的持续时间内拥有一个互斥量;
特点:
1)创建即加锁,作用域结束自动析构并解锁,无需手工解锁
2)不能中途解锁,必须等作用域结束才解锁
3)不能复制

std::mutex g_lock;
void func()
{
	std::lock_guard<std::mutex> locker(g_lock); //出作用域自动解锁
	....
}

std::unique_lock详解
unique_lock是个类模板,unique_lock比lock_guard灵活很多,效率上差一点,内存占用多一点
它允许延迟锁定,限时深度锁定,递归锁定,锁定所有权的转移以及与条件变量一起使用。

特点如下:
1)创建时可以不锁定(通过指定第二个参数为std::defer_lock),而在需要时再锁定
2)可以随时加锁解锁
3)作用域规则同 lock_grard,析构时自动释放锁
4)不可复制,可移动(移动语义)
5)条件变量需要该类型的锁作为参数(此时必须使用unique_lock)

std::mutex m;
std::unique_lock<std::mutex> lock2(m, std::defer_lock);  //只是单单的用m初始化lock,但是不进行加锁
std::unique_lock<std::mutex> lock(m, std::chrono::seconds(1));   //在1秒钟之后,对m进行加锁,如果加锁成功就返回,加锁失败就阻塞等待
std::unique_lock<std::mutex> lock(m, std::try_to_lock);//尝试加锁m,但是不阻塞

std::try_to_lock():会尝试加锁,使用前不能自己lock()
std::defer_lock():可以不给mutex加锁,相当于初始化了一个没有加锁的mutex,前提是先不能lock().
std::try_lock():尝试给锁上锁,如果上锁失败,返回false,否则返回true
std::lock():允许你一次锁定多个mutex,会锁住它收到的所有mutex,并且阻塞到所有的mutex都被锁定或直到抛出异常才返回
std::release():返回它管理的mutex对象指针,并释放所有权;如果你release之前,已经加锁了,release之后你要负责unlock()

unique_lock所有权的传递

std::unique_lock<std::mutex> sbguard(my_mutex);//所有权概念
//sbguard拥有my_mutex的所有权;sbguard可以把自己对mutex(my_mutex)的所有权转移给其他的unique_lock对象;所以unique_lock对象这个mutex的所有权是可以转移,但是不能复制。

std::unique_lock<std::mutex> sbguard1(my_mutex);
std::unique_lock<std::mutex> sbguard2(sbguard1);//此句是非法的,复制所有权是非法的

//方法一:通过std::move实现所有权传递
std::unique_lock<std::mutex> sbguard2(std::move(sbguard));//移动语义,现在先当与sbguard2与my_mutex绑定到一起了
//现在sbguard1指向空,sbguard2指向了my_mutex

//方法二:
std::unique_lock<std::mutex> rtn_unique_lock()
{
	std::unique_lock<std::mutex> tmpguard(my_mutex);
	return tmpguard;//从函数中返回一个局部的unique_lock对象是可以的。三章十四节讲解过移动构造函数。
	//返回这种举报对象tmpguard会导致系统生成临时unique_lock对象,并调用unique_lock的移动构造函数
}
void inMsgRecvQueue()
{
	for (int i = 0; i < 10000; i++)
	{
		std::unique_lock<std::mutex> sbguard1 = rtn_unique_lock();
		msgRecvQueue.push_back(i);
	}
}

2.2、recursive_mutex

允许同一个线程多次获得该互斥锁,解决同一个线程多次获取互斥量的死锁问题

class X {
    std::recursive_mutex m;
    std::string shared;
  public:
    void fun1() {
      std::lock_guard<std::recursive_mutex> lk(m);
      shared = "fun1";
      std::cout << "in fun1, shared variable is now " << shared << '\n';
    }
    void fun2() {
      std::lock_guard<std::recursive_mutex> lk(m);
      shared = "fun2";
      std::cout << "in fun2, shared variable is now " << shared << '\n';
      fun1(); // ① 递归锁在此处变得有用
      std::cout << "back in fun2, shared variable is " << shared << '\n';
    };
};
int main() 
{
    X x;
    std::thread t1(&X::fun1, &x);
    std::thread t2(&X::fun2, &x);
    t1.join();
    t2.join();
}

注意点:
1)允许递归互斥容易放纵复杂逻辑的产生,导致一些多线程同步引起的问题,而这部分逻辑往往是可以优化处理的
2)递归锁的效率比非递归锁的要低
3)递归锁虽允许同一个线程多次获取同一个互斥锁,但由一定次数限制,一旦超过则进行调用的时候会抛出std::system错误

2.3、recursive_timed_mutex和timed_mutex

timed_mutex是超时独占锁;
recursive_timed_mutex:带超时的递归锁
std::try_lock_for();是等待一段时间,参数是时间段
std::try_lock_until();参数是一个时间点,到那个时间点之前等待获取锁

3、条件变量

同步等待机制、阻塞单个或多个线程,直到另外一个线程发通知或者超时,唤醒当前阻塞线程。需要和互斥量一起使用
condition_variable:配合unique_lock使用进行wait操作
condition_variable_any:和任意锁配合使用,比较灵活,但效率比condition_variable低

函数:
notify_one-- 通知一个等待线程
notify_all-- 通知所有等待线程
wait – 阻塞当前线程,直到条件变量被唤醒,
wait_for – 阻塞当前线程,直到条件变量被唤醒或到指定时间时长
wait_until – 阻塞当前线程,直到条件变量被唤醒或到指定时间点

过程简述:
1)一个线程因等待"条件变量的条件成立"而挂起;(condition_variable类成员wait 、wait_for 或 wait_until)
2)另外一个线程使"条件成立",给出信号,从而唤醒被等待的线程(使用的是condition_variable类成员notify_one或者notify_all函数)

注意点:
实际中发现,因此操作系统的原因,wait类型在不满足条件时,它也会返回,这就导致了虚假唤醒

while (!(xxx条件) )
{
    //虚假唤醒发生,由于while循环,再次检查条件是否满足,
    //否则继续等待,解决虚假唤醒
    wait();  
}

4、原子量

std::atomic,使用了原子量就不使用互斥量

template < class T > struct atomic {
    //判断atomic<T>中的T对象是否为lock_free
    bool is_lock_free() const volatile;
    bool is_lock_free() const;
    
    //基本的构造函数,重载函数
    atomic() = default;
    constexpr atomic(T val);
    atomic(const atomic &) = delete;
    atomic & operator=(const atomic &) = delete;
    atomic & operator=(const atomic &) volatile = delete;
    T operator=(T val) volatile;
    T operator=(T val);
    operator  T() const volatile;
    operator  T() const;
    
    //一系列封装的原子操作
    T exchange(T val, memory_order = memory_order_seq_cst) volatile;
    T exchange(T val, memory_order = memory_order_seq_cst);
    void store(T val, memory_order = memory_order_seq_cst) volatile;
    void store(T val, 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;
    bool compare_exchange_weak(T& expected, T val, 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);
};

使用举例:

#include <iostream>
#include <atomic>
#include <vector>
#include <functional>
#include <thread>
using namespace std;

std::atomic<long> sum;
const int count = 100000;

void add()
{
    //每个线程来循环对sum实行加1操作
    for (int j = 0; j < count; ++j)
        sum++;
}

int main()
{
    std::vector<std::thread> threads;
    //开始计时
    clock_t start = clock();

    for (int j = 0; j < 5; ++j)
        threads.push_back(std::move(std::thread(add)));

    for (auto & t : threads)
        t.join();
    //结束计时
    clock_t finish = clock();
    cout<<"result:"<<sum<<endl;
    cout<<"time:"<<finish -start<<"ms"<<endl;
    return 0;
}

//输出结果:result:50000,time:31290ms

内存序

 bool compare_exchange_weak(T& expected, T val, memory_order = memory_order_seq_cst) volatile;

 //6种类型的内存序, 用于控制变量在不同线程间的顺序可见性
 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;

5、call_once和once_flag的使用

保证函数在多线程的环境中只被调用一次

std::once_flag flag;
void do_once()
{
	std::call_one(flag, [](){std::cout<<"Called once"<<std::endl;});
}
int main(void)
{ 
	std::thread t1(do_once);
	std::thread t2(do_once);
	std::thread t3(do_once);
	t1.join();		
	t2.join();
	t3.join();
	//输出:Called once
}

注意点:once_flag的生命周期,它必须要比使用它的线程的生命周期要长。所以通常定义成全局变量比较好

//完美的解决线程安全的单例模式
#include <iostream>
#include <memory>
#include <mutex>

class Singleton {
public:
  static Singleton& GetInstance() {
    static std::once_flag s_flag;
    std::call_once(s_flag, [&]() {
      instance_.reset(new Singleton);
    });
    return *instance_;
  }

  ~Singleton() = default;
  void PrintAddress() const {
    std::cout << this << std::endl;
  }
private:
  Singleton() = default;
  Singleton(const Singleton&) = delete;
  Singleton& operator=(const Singleton&) = delete;
private:
  static std::unique_ptr<Singleton> instance_;
};

std::unique_ptr<Singleton> Singleton::instance_;
int main() {
  Singleton& s1 = Singleton::GetInstance();
  s1.PrintAddress();
  Singleton& s2 = Singleton::GetInstance();
  s2.PrintAddress();
  return 0;
}

6、异步操作

让调用方法的主线程不需要同步等待调用函数,从而可以让主线程继续执行它下面的代码(回调函数)
当需要执行I/O操作时,使用异步操作比使用线程+同步 I/O操作更合适。
C++11中的异步操作主要有std::future、std::async、std::promise、std::packaged_task

6.1、std::future

future对象是std::async、std::promise、std::packaged_task的底层对象,用来传递其他线程中操作的数据结果

#include <future>
template< class T >  class future;
template< class T >  class future<T&>;
template<>        class future<void>;

成员函数:
share – 从 *this 转移共享状态给 shared_future 并返回它
get – 返回结果
valid – 检查 future 是否拥有共享状态
wait – 等待结果变得可用
wait_for – 等待结果,如果在指定的超时间隔后仍然无法得到结果,则返回
wait_until – 等待结果,如果在已经到达指定的时间点时仍然无法得到结果,则返回

异步访问机制:
类模板std::future提供访问异步操作结果的机制:
1)通过 std::promise、std::packaged_task和std::async创建的异步操作能提供一个 std::future 对象给该异步操作的创建者。
2)异步操作的创建者能用各种方法查询、等待或从 std::future 提取值。若异步操作仍未提供值,则这些方法可能阻塞。
3)异步操作准备好发送结果给创建者时,它能通过修改链接到创建者的 std::future 的共享状态(例如 std::promise::set_value )进行

注意点:
1)std::future 所引用的共享状态不与另一异步返回对象共享(与 std::shared_future 相反)
2)std::asyanc是std::future的高级封装, 一般我们不会直接使用std::futrue,而是使用对std::future的高级封装std::async

#include <iostream>
#include <future>
#include <thread>
 
int main()
{
    // 来自 packaged_task 的 future
    std::packaged_task<int()> task([](){ return 7; }); 		// 包装函数
    std::future<int> f1 = task.get_future();  			// 获取 future
    std::thread(std::move(task)).detach(); 			// 在线程上运行
 
    // 来自 async() 的 future
    std::future<int> f2 = std::async(std::launch::async, [](){ return 8; });
 
    // 来自 promise 的 future
    std::promise<int> p;
    std::future<int> f3 = p.get_future();
    std::thread( [&p]{ p.set_value_at_thread_exit(9); }).detach();
 
    std::cout << "Waiting..." << std::flush;
    f1.wait();
    f2.wait();
    f3.wait();
    std::cout << "Done!\nResults are: "<< f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n';
    
	system("pause");
	return 0;
}

//输出
//Waiting...Done!
//Results are: 7 8 9

6.2、std::promise

std::promise提供了不同线程之间的数据同步机制,可存储一个某种类型的值,并将其传递给对应的future,即使这个future不在同一个线程中也可以访问到这个值
注意点:取值是间接的通过promise内部提供的future来获取的

#include<future>
template< class R > class promise;
template< class R > class promise<R&>;
template<>          class promise<void>;

应用场景:

  1. 空模板
  2. 非 void 特化,用于在线程间交流对象
  3. void 特化,用于交流无状态事件

promise 可以对共享状态做三件事:
1)使就绪: promise 存储结果或异常于共享状态。标记共享状态为就绪,并解除阻塞任何等待于与该共享状态关联的 future 上的线程。
2)释放: promise 放弃其对共享状态的引用。若这是最后一个这种引用,则销毁共享状态。除非这是 std::async 所创建的未就绪的共享状态,否则此操作不阻塞。
3)抛弃: promise 存储以 std::future_errc::broken_promise 为 error_code 的 std::future_error 类型异常,令共享状态为就绪,然后释放它。

成员函数:
swap – 交换二个 promise 对象
get_future – 返回与承诺结果关联的future
set_value – 设置结果为指定值
set_value_at_thread_exit – 设置结果为指定值,同时仅在线程退出时分发提醒
set_exception – 设置结果为指示异常
set_exception_at_thread_exit – 设置结果为指示异常,同时仅在线程退出时分发提醒

c++11系统函数:
std::swap(std::promise)-- 特化 std::swap 算法(函数模板)
std::uses_allocatorstd::promise(C++11) – 特化 std::uses_allocator 类型特征 (类模板特化)

#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <iostream>
#include <chrono>
 
void accumulate(std::vector<int>::iterator first,
	std::vector<int>::iterator last,
	std::promise<int> accumulate_promise)
{
	int sum = std::accumulate(first, last, 0);
	accumulate_promise.set_value(sum);			// 提醒 future
}
 
void do_work(std::promise<void> barrier)
{
	std::this_thread::sleep_for(std::chrono::seconds(1));
	barrier.set_value();
}
 
int main()
{
	// 演示用 promise<int> 在线程间传递结果。
	std::vector<int> numbers = { 1, 2, 3, 4, 5, 6 };
	std::promise<int> accumulate_promise;
	std::future<int> accumulate_future = accumulate_promise.get_future();	// 传递其他线程中操作结果
	std::thread work_thread(accumulate, numbers.begin(), numbers.end(),
		std::move(accumulate_promise));
	accumulate_future.wait();	// 等待结果
	std::cout << "result=" << accumulate_future.get() << '\n';
	work_thread.join();			// 阻塞等待线程完成
 
	// 演示用 promise<void> 在线程间对状态发信号
	std::promise<void> barrier;
	std::future<void> barrier_future = barrier.get_future();
	std::thread new_work_thread(do_work, std::move(barrier));
	barrier_future.wait();		// 等待结果
	new_work_thread.join();		// 阻塞等待线程完成
 
	system("pause");
	return 0;
}
//输出:result=21

6.3、std::packaged_task

std::packaged_task的作用是为可调用的对象(函数,lambda表达式,bind表达式或者其他表达式)提供一个不同线程之间的数据同步机制,它可以存储一个函数操作,并将其返回值传递给对应的future,而这个future在另一个线程中也可以安全的访问这个值

#include<future>
template< class > class packaged_task; // 不定义
template< class R, class ...Args > 
class packaged_task<R(Args...)>;

成员函数:
valid – 检查任务对象是否拥有合法函数
swap – 交换二个任务对象
get_future – 返回与承诺的结果关联的 std::future
operator() – 执行函数
make_ready_at_thread_exit – 执行函数,并确保结果仅在一旦当前线程退出时就绪
reset – 重置状态,抛弃任何先前执行的存储结果

c++11系统函数:
std::swap(std::packaged_task)(C++11) – 特化 std::swap 算法(函数模板)
std::uses_allocatorstd::packaged_task(C++11)(C++17 前) – 特化 std::uses_allocator 类型特征 (类模板特化)

#include <iostream>
#include <cmath>
#include <thread>
#include <future>
#include <functional>
 
// 避免对 std::pow 重载集消歧义的独有函数
int f(int x, int y) { return std::pow(x, y); }
 
void task_lambda()
{
	std::packaged_task<int(int, int)> task([](int a, int b) {
		return std::pow(a, b);
	});
	std::future<int> result = task.get_future();
 
	task(2, 9);
 
	std::cout << "task_lambda:\t" << result.get() << '\n';
}
 
void task_bind()
{
	std::packaged_task<int()> task(std::bind(f, 2, 11));
	std::future<int> result = task.get_future();
 
	task();
 
	std::cout << "task_bind:\t" << result.get() << '\n';
}
 
void task_thread()
{
	std::packaged_task<int(int, int)> task(f);
	std::future<int> result = task.get_future();
 
	std::thread task_td(std::move(task), 2, 10);
	task_td.join();
 
	std::cout << "task_thread:\t" << result.get() << '\n';
}
 
int main()
{
	task_lambda();
	task_bind();
	task_thread();
 
	system("pause");
	return 0;
}
//输出
//task_lambda:    512
//task_bind:      2048
//task_thread:    1024

6.4、std::async

std::async用于创建异步任务,实际上就是创建一个线程执行相应任务
std::async就是异步编程的高级封装,封装了std::future的操作,基本上可以代替std::thread 的所有事情
std::async的操作,其实相当于封装了std::promise、std::packaged_task加上std::thread。

#include<future>
template< class Function, class... Args >
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
    async( std::launch policy, Function&& f, Args&&... args );

参数:
policy:线程的创建策略:一种是调用即创建线程(std::launch::async);另一种是延迟加载方式创建线程(std::launch::deferred),调用时不创建线程,直到调用future的get或者wait时才创建线程。
f:线程函数
args:线程函数的参数
返回值:
指代此次调用 std::async 所创建的共享状态的 std::future

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <future>
#include <string>
#include <mutex>
 
std::mutex m;	// 互斥量
struct X {
	void foo(int i, const std::string& str) {
		std::lock_guard<std::mutex> lk(m);
		std::cout << str << ' ' << i << '\n';
	}
	void bar(const std::string& str) {
		std::lock_guard<std::mutex> lk(m);
		std::cout << str << '\n';
	}
	int operator()(int i) {
		std::lock_guard<std::mutex> lk(m);
		std::cout << i << '\n';
		return i + 10;
	}
};
 
template <typename RandomIt>
int parallel_sum(RandomIt beg, RandomIt end)
{
	auto len = end - beg;
	if (len < 1000)
		return std::accumulate(beg, end, 0);
 
	RandomIt mid = beg + len / 2;
	auto handle = std::async(std::launch::async,parallel_sum<RandomIt>, mid, end);
	int sum = parallel_sum(beg, mid);
	return sum + handle.get();
}
 
int main()
{
	std::vector<int> v(10000, 1);
	std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';
 
	X x;
	// 以默认策略调用 x.foo(42, "Hello") :
	// 可能同时打印 "Hello 42" 或延迟执行
	auto a1 = std::async(&X::foo, &x, 42, "Hello");
 
	// 以 deferred 策略调用 x.bar("world!")
	// 调用 a2.get() 或 a2.wait() 时打印 "world!"
	auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!");
 
	// 以 async 策略调用 X()(43) :
	// 同时打印 "43"
	auto a3 = std::async(std::launch::async, X(), 43);
	a2.wait();                     // 打印 "world!"
	std::cout << a3.get() << '\n'; // 打印 "53"
 
	system("pause");
	return 0;
 
} // 若 a1 在此点未完成,则 a1 的析构函数在此打印 "Hello 42"

//输出
//The sum is 10000
//Hello 42
//world!
//43
//53

std::launch::async,表示立即开启异步求值
std::launch::deferred,延迟开启,只有当返回的future实例调用get函数时才开启异步求值
而默认情况下的异步属性是std::launch::async | std::launch::deferred,所以到底是立即开启还是延迟开启取决于编译器的不同。如果异步至关重要的话记得在构造函数中指定std::launch::asyn

注意点:std::async替代线程的创建,作为异步操作的首选

6.5、总结

future提供访问异步操作结果的机制,与线程是同一级别
packaged_task和promise将future用作异步操作或者异步结果的连接通道,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于论坛缺少好用支持库且易本身C++版本过低导致很多C++特性无法使用。本次利用C++11/14的标准库以及一些C++知名库(RapidJson,Curl)编写支持库使用,以至于编程上不会太落后。 C++11/14标准库相对于微软类库而言与微软无关,可实现跨平台。且其拥有很多高级语法,其效率及稳定性毋庸置疑。如果能直接用标准库完成坚决不要重复造轮子。     此次封装了线程线程池、哈希表(UnOrderedMap)、读写锁、互斥、定时器、计时器、Json、Curl等。其中Json封装于RapidJson,此库为C++最快的Json库,效率高于论坛其他工具几百倍。 Curl为知名Http库,很多公司及个人都是首选。     由于易语言5.6版本核心库与其他版本不太一样导致静态编译过程中出现一些问题,所以请大家最好不要使用5.6版本。由于使用到了高版本C++库所以易语言自带的VC6编译器肯定不能编译, 在此本支持库使用了论坛的VS2014编译器,完美实现静态编译,如果你本身有这个编译器也请一定用本次配套的替换使用,否则会出现少库情况。     至于编译出来的程序能否支持XP,我想说这是肯定的,具体操作方法请参见压缩包里的说明。 备用地址 :链接:https://pan.baidu.com/s/1gY8Gm_kxMH1GOZiYaZeYZw   提取码:u1hs (里面包含编译器,支持库,例程) 文件较大,已包含 编辑所需的链接器

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值