标签:ast 视图 tail names call 代码 自动 lis start
如图以下是头文件<future>中的类容。
future有两个类模板,一个独占的std::future,也就是只能被获取一次,另一个是共享的std::shared_future。std::future<T>是一个类模板,其中T是要存储的值的类型,std::future 的实例只能与一个指定事件相关联。std::future对象在内部存储一个将来会被赋值的值,并提供了一个访问该值的机制,通过get()成员函数实现。但如果有人视图在get()函数可用之前通过它来访问相关的值,那么get()函数将会阻塞,直到该值可用。std::future的一个对象,可以从某个对象(std::promise和std::packaged_task)或函数(std::async())获取值,并在不同线程之间提供恰当的同步访问。如std::async 会返回一个 std::future 对象,这个对象持有最终计算出来的结果。当你需要这个值时,你只需要调用这个对象的get()成员函数,他会阻塞到当前位置,知道获取到那个返回的值。如前所述,std::future通常与std::async()函数和std::promise、std::packaged_tast对象一起使用。
1 // future example 2 #include <iostream> // std::cout 3 #include <future> // std::async, std::future 4 #include <chrono> // std::chrono::milliseconds 5 6 // a non-optimized way of checking for prime numbers: 7 bool is_prime (int x) { 8 for (int i=2; i<x; ++i) if (x%i==0) return false; 9 return true; 10 } 11 12 int main () 13 { 14 // call function asynchronously: 15 std::future<bool> fut = std::async (is_prime,444444443); 16 17 // do something while waiting for function to set future: 18 std::cout << "checking, please wait"; 19 std::chrono::milliseconds span (100); 20 while (fut.wait_for(span)==std::future_status::timeout) 21 std::cout << ‘.‘ << std::flush; 22 23 bool x = fut.get(); // retrieve return value 24 25 std::cout << "\n444444443 " << (x?"is":"is not") << " prime.\n"; 26 27 return 0; 28 }
std::future_status::ready
std::future_status::timeout
std::future_status::deferred
std::async()是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,它返回一个std::future对象。std::async()使用方法和std::thread()相同。
*std::thread产生的线程需要在主线程中调用需要join或者detach,否则会出现异常,而std::async产生的线程不需要我们做任何处理。
1 #include <iostream> 2 #include <future> 3 using namespace std; 4 class A { 5 public: 6 int mythread(int mypar) { 7 cout << mypar << endl; 8 return mypar; 9 } 10 }; 11 12 13 int mythread() { 14 cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl; 15 std::chrono::milliseconds dura(5000); 16 std::this_thread::sleep_for(dura); 17 cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl; 18 return 5; 19 } 20 21 22 int main() { 23 A a; 24 int tmp = 12; 25 cout << "main" << "threadid = " << std::this_thread::get_id() << endl; 26 std::future<int> result1 = std::async(mythread); 27 cout << "continue........" << endl; 28 cout << result1.get() << endl; //阻塞在这里等待mythread()执行完毕,拿到结果 29 30 //类成员函数 31 std::future<int> result2 = std::async(&A::mythread, &a, tmp); //第二个参数是对象引用才能保证线程里执行的是同一个对象 32 cout << result2.get() << endl; 33 //或者result2.wait(); 34 cout << "good luck" << endl; 35 return 0; 36 }
我们通过向std::async()传递一个参数,改参数是std::launch类型(枚举类型),来达到一些特殊的目的:
1、std::lunch::deferred,(defer推迟,延期)表示线程入口函数的调用会被延迟,一直到std::future的wait()或者get()函数被调用时(由主线程调用)才会执行;如果wait()或者get()没有被调用,则不会执行。
实际上根本就没有创建新线程。std::lunch::deferred意思时延迟调用,并没有创建新线程,是在主线程中调用的线程入口函数。
2、std::launch::async,在调用async函数的时候就开始创建新线程。就是std::async()的默认情况。
1 #include <iostream> 2 #include <future> 3 using namespace std; 4 5 int mythread() { 6 cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl; 7 std::chrono::milliseconds dura(5000); 8 std::this_thread::sleep_for(dura); 9 cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl; 10 return 5; 11 } 12 13 14 int main() { 15 cout << "main" << "threadid = " << std::this_thread::get_id() << endl; 16 std::future<int> result1 = std::async(std::launch::deferred ,mythread); 17 cout << "continue........" << endl; 18 cout << result1.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果 19 cout << "good luck" << endl; 20 return 0; 21 }
还有让std::future 与一个任务实例相关联的唯一方式,可以将任务包装入一个 std::packaged_task<> 实例中,或使用 std::promise<> 类型模板显示设置值。与 std::promise<> 对比, std::packaged_task<> 具有更高层的抽象。
std::promise也是一个类模板,其对象有可能在将来对值进行赋值,每个std::promise对象有一个对应的std::future对象, std::promise保存的值可被与之关联的std::future读取,读取操作可以发生在其它线程。std::promise允许move语义(右值构造,右值赋值),但不允许拷贝(拷贝构造、赋值),std::future亦然。std::promise<void>
是合法的,此时std::promise.set_value不接受任何参数,仅用于通知关联的std::future.get()解除阻塞。
std::promise和std::future合作共同实现了多线程间通信。
1 #include <iostream> 2 #include <thread> 3 #include <future> 4 #include <chrono> 5 6 // 线程B 7 void initiazer(std::promise<int> * promObj) 8 { 9 std::cout << "Thread B" << std::endl; 10 // set the value at proper time 11 std::this_thread::sleep_for(std::chrono::seconds(3)); 12 promObj->set_value(23); 13 } 14 15 int main() 16 { 17 // 线程A 18 std::promise<int> promiseObj; 19 std::future<int> futureObj = promiseObj.get_future(); 20 21 std::thread th(initiazer, &promiseObj); // 启动线程B 22 23 // 获取对象的值,该调用在B设置其值后会返回23,在B设置其值前会阻塞 24 std::cout<< futureObj.get() << std::endl; 25 26 th.join(); 27 28 return 0; 29 } 30 31 //输出23
1 #include <iostream> // std::cout, std::endl 2 #include <thread> // std::thread 3 #include <string> // std::string 4 #include <future> // std::promise, std::future 5 #include <chrono> // seconds 6 using namespace std::chrono; 7 //线程B 8 void read(std::future<std::string> *future) { 9 // future会一直阻塞,直到有值到来 10 std::cout << future->get() << std::endl; 11 } 12 //线程A 13 int main() { 14 // promise 相当于生产者 15 std::promise<std::string> promise; 16 // future 相当于消费者, 右值构造 17 std::future<std::string> future = promise.get_future(); 18 // 另一线程中通过future来读取promise的值 19 std::thread thread(read, &future); 20 // 让read等一会儿:) 21 std::this_thread::sleep_for(seconds(1)); 22 // 23 promise.set_value("hello future"); 24 // 等待线程执行完成 25 thread.join(); 26 27 return 0; 28 } 29 // 控制台输: hello future
如上代码中,一旦std::promise对象调用set_value设置了对象的值,该对象的共享状态就变更为ready,std::future对象就能使用get()函数获取到值。
注意:
1 #include <iostream> // std::cout, std::endl 2 #include <thread> // std::thread 3 #include <future> // std::promise, std::future 4 #include <chrono> // seconds 5 using namespace std::chrono; 6 7 void read(std::future<int> future) { 8 try { 9 future.get(); 10 } catch(std::future_error &e) { 11 std::cerr << e.code() << "\n" << e.what() << std::endl; 12 } 13 } 14 15 int main() { 16 std::thread thread; 17 { 18 // 如果promise不设置任何值 19 // 则在promise析构时会自动设置为future_error 20 // 这会造成future.get抛出该异常 21 std::promise<int> promise; 22 thread = std::thread(read, promise.get_future()); 23 } 24 thread.join(); 25 26 return 0; 27 }
通过std::promise::set_exception函数可以设置自定义异常,该异常最终会被传递到std::future,并在其get函数中被抛出。
1 #include <iostream> 2 #include <future> 3 #include <thread> 4 #include <exception> // std::make_exception_ptr 5 #include <stdexcept> // std::logic_error 6 7 void catch_error(std::future<void> &future) { 8 try { 9 future.get(); 10 } catch (std::logic_error &e) { 11 std::cerr << "logic_error: " << e.what() << std::endl; 12 } 13 } 14 15 int main() { 16 std::promise<void> promise; 17 std::future<void> future = promise.get_future(); 18 19 std::thread thread(catch_error, std::ref(future)); 20 // 自定义异常需要使用make_exception_ptr转换一下 21 promise.set_exception( 22 std::make_exception_ptr(std::logic_error("caught"))); 23 24 thread.join(); 25 return 0; 26 } 27 // 输出:logic_error: caught
std::packaged_task 包装一个可调用的对象,并且允许异步获取该可调用对象产生的结果,从包装可调用对象意义上来讲,std::packaged_task 与 std::function 类似,只不过 std::packaged_task 将其包装的可调用对象的执行结果传递给一个 std::future 对象(该对象通常在另外一个线程中获取 std::packaged_task 任务的执行结果)。
std::packaged_task 对象内部包含了两个最基本元素,一、被包装的任务(stored task),任务(task)是一个可调用的对象,如函数指针、成员函数指针或者函数对象,二、共享状态(shared state),用于保存任务的返回值,可以通过 std::future 对象来达到异步访问共享状态的效果。
可以通过 std::packged_task::get_future 来获取与共享状态相关联的 std::future 对象。在调用该函数之后,两个对象共享相同的共享状态,具体解释如下:
std::packaged_task 的共享状态的生命周期一直持续到最后一个与之相关联的对象被释放或者销毁为止。
1 #include <iostream> // std::cout 2 #include <future> // std::packaged_task, std::future 3 #include <chrono> // std::chrono::seconds 4 #include <thread> // std::thread, std::this_thread::sleep_for 5 6 // count down taking a second for each value: 7 int countdown (int from, int to) { 8 for (int i=from; i!=to; --i) { 9 std::cout << i << ‘\n‘; 10 std::this_thread::sleep_for(std::chrono::seconds(1)); 11 } 12 std::cout << "Finished!\n"; 13 return from - to; 14 } 15 16 int main () 17 { 18 std::packaged_task<int(int,int)> task(countdown); // 设置 packaged_task 19 std::future<int> ret = task.get_future(); // 获得与 packaged_task 共享状态相关联的 future 对象. 20 21 std::thread th(std::move(task), 10, 0); //创建一个新线程完成计数任务. 22 23 int value = ret.get(); // 等待任务完成并获取结果. 24 25 std::cout << "The countdown lasted for " << value << " seconds.\n"; 26 27 th.join(); 28 return 0; 29 }
std::shared_future:也是个类模板,可以让多个线程等待同一个事件,用法和std::future差不多。区别是std::future的 get() 成员函数是转移数据,只能get()一次; std::shared_future 的 get()成员函数是复制数据,可以get()多次。
获取多次
1 #include <thread> 2 #include <iostream> 3 #include <future> 4 using namespace std; 5 6 int mythread() { 7 cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl; 8 std::chrono::milliseconds dura(5000); 9 std::this_thread::sleep_for(dura); 10 cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl; 11 return 5; 12 } 13 14 int main() { 15 cout << "main" << "threadid = " << std::this_thread::get_id() << endl; 16 std::packaged_task<int()> mypt(mythread); 17 std::thread t1(std::ref(mypt)); 18 std::future<int> result = mypt.get_future(); 19 20 bool ifcanget = result.valid(); //判断future 中的值是不是一个有效值 21 std::shared_future<int> result_s(result.share()); //执行完毕后result_s里有值,而result里空了 22 //std::shared_future<int> result_s(std::move(result)); 23 //通过get_future返回值直接构造一个shared_future对象 24 //std::shared_future<int> result_s(mypt.get_future()); 25 t1.join(); 26 27 auto myresult1 = result_s.get(); 28 auto myresult2 = result_s.get(); 29 30 cout << "good luck" << endl; 31 return 0; 32 }
在每一个 std::shared_future 的独立对象上成员函数调用返回的结果还是不同步的,所以为了在多个线程访问一个独立对象时,避免数据竞争,必须使用锁来对访问进行保护。优先使用的办法:为了替代只有一个拷贝对象的情况,可以让每个线程都拥有自己对应的拷贝对象。这样,当每个线程都通过自己拥有的 std::shared_future 对象获取结果,那么多个线程访问共享同步结果就是安全的。
http://www.cplusplus.com/reference/future/
https://blog.csdn.net/qq_38231713/article/details/106092879
https://www.jianshu.com/p/7945428c220e
【C++多线程】std::future、std::async、std::promise、std::packaged_task、std::shared_future
标签:ast 视图 tail names call 代码 自动 lis start
原文地址:https://www.cnblogs.com/chen-cs/p/13252591.html