【C++标准库】并发
高级接口async()和future
/* The following code example is taken from the book * "The C++ Standard Library - A Tutorial and Reference, 2nd Edition" * by Nicolai M. Josuttis, Addison-Wesley, 2012 * * (C) Copyright Nicolai M. Josuttis 2012. * Permission to copy, use, modify, sell and distribute this software * is granted provided this copyright notice appears in all copies. * This software is provided "as is" without express or implied * warranty, and with no claim as to its suitability for any purpose. */ #include <future> #include <thread> #include <chrono> #include <random> #include <iostream> #include <exception> using namespace std; int doSomething(char c) { // random-number generator (use c as seed to get different sequences) std::default_random_engine dre(c); std::uniform_int_distribution<int> id(10, 1000); // loop to print character after a random period of time for (int i = 0; i<10; ++i) { this_thread::sleep_for(chrono::milliseconds(id(dre))); cout.put(c).flush(); } return c; } int func1() { return doSomething('.'); } int func2() { return doSomething('+'); } int main() { std::cout << "starting func1() in background" << " and func2() in foreground:" << std::endl; // start func1() asynchronously (now or later or never): std::future<int> result1(std::async(func1)); int result2 = func2(); // call func2() synchronously (here and now) // print result (wait for func1() to finish and add its result to result2 int result = result1.get() + result2; std::cout << "\nresult of func1()+func2(): " << result << std::endl; }
class std::future提供了“处理并发运算之未来结果”的能力,但是get()只能调用一次,第二次调用get()会导致不可预期的行为。然而有时候,多次处理并发结果是合理的,特别是当多个线程都想处理这个结果时。基于这个目的,C++标准库提供了class std::shared_future,可以多次调用get(),得到相同的结果,或导致抛出一个异常。
/* The following code example is taken from the book * "The C++ Standard Library - A Tutorial and Reference, 2nd Edition" * by Nicolai M. Josuttis, Addison-Wesley, 2012 * * (C) Copyright Nicolai M. Josuttis 2012. * Permission to copy, use, modify, sell and distribute this software * is granted provided this copyright notice appears in all copies. * This software is provided "as is" without express or implied * warranty, and with no claim as to its suitability for any purpose. */ #include <future> #include <thread> #include <iostream> #include <exception> #include <stdexcept> using namespace std; int queryNumber() { // read number cout << "read number: "; int num; cin >> num; // throw exception if none if (!cin) { throw runtime_error("no number read"); } return num; } void doSomething(char c, shared_future<int> f) { try { // wait for number of characters to print int num = f.get(); // get result of queryNumber() for (int i = 0; i<num; ++i) { this_thread::sleep_for(chrono::milliseconds(100)); cout.put(c).flush(); } } catch (const exception& e) { cerr << "EXCEPTION in thread " << this_thread::get_id() << ": " << e.what() << endl; } } int main() { try { // start one thread to query a number shared_future<int> f = async(queryNumber); // start three threads each processing this number in a loop auto f1 = async(launch::async, doSomething, '.', f); auto f2 = async(launch::async, doSomething, '+', f); auto f3 = async(launch::async, doSomething, '*', f); // wait for all loops to be finished f1.get(); f2.get(); f3.get(); } catch (const exception& e) { cout << "\nEXCEPTION: " << e.what() << endl; } cout << "\ndone" << endl; }
低层接口:Thread和Promise
/* The following code example is taken from the book * "The C++ Standard Library - A Tutorial and Reference, 2nd Edition" * by Nicolai M. Josuttis, Addison-Wesley, 2012 * * (C) Copyright Nicolai M. Josuttis 2012. * Permission to copy, use, modify, sell and distribute this software * is granted provided this copyright notice appears in all copies. * This software is provided "as is" without express or implied * warranty, and with no claim as to its suitability for any purpose. */ #include <thread> #include <chrono> #include <random> #include <iostream> #include <exception> using namespace std; void doSomething(int num, char c) { try { // random-number generator (use c as seed to get different sequences) default_random_engine dre(42 * c); uniform_int_distribution<int> id(10, 1000); for (int i = 0; i<num; ++i) { this_thread::sleep_for(chrono::milliseconds(id(dre))); cout.put(c).flush(); //... } } // make sure no exception leaves the thread and terminates the program catch (const exception& e) { cerr << "THREAD-EXCEPTION (thread " << this_thread::get_id() << "): " << e.what() << endl; } catch (...) { cerr << "THREAD-EXCEPTION (thread " << this_thread::get_id() << ")" << endl; } } int main() { try { thread t1(doSomething, 5, '.'); // print five dots in separate thread cout << "- started fg thread " << t1.get_id() << endl; // print other characters in other background threads for (int i = 0; i<5; ++i) { thread t(doSomething, 10, 'a' + i); // print 10 chars in separate thread cout << "- detach started bg thread " << t.get_id() << endl; t.detach(); // detach thread into the background } cin.get(); // wait for any input (return) cout << "- join fg thread " << t1.get_id() << endl; t1.join(); // wait for t1 to finish } catch (const exception& e) { cerr << "EXCEPTION: " << e.what() << endl; } }
/* The following code example is taken from the book * "The C++ Standard Library - A Tutorial and Reference, 2nd Edition" * by Nicolai M. Josuttis, Addison-Wesley, 2012 * * (C) Copyright Nicolai M. Josuttis 2012. * Permission to copy, use, modify, sell and distribute this software * is granted provided this copyright notice appears in all copies. * This software is provided "as is" without express or implied * warranty, and with no claim as to its suitability for any purpose. */ #include <thread> #include <future> #include <iostream> #include <string> #include <exception> #include <stdexcept> #include <functional> #include <utility> void doSomething(std::promise<std::string>& p) { try { // read character and throw exception if 'x' std::cout << "read char ('x' for exception): "; char c = std::cin.get(); if (c == 'x') { throw std::runtime_error(std::string("char ") + c + " read"); } //... std::string s = std::string("char ") + c + " processed"; p.set_value_at_thread_exit(std::move(s)); // store result } catch (...) { p.set_exception_at_thread_exit(std::current_exception()); // store exception } } int main() { try { // create a promise to store the outcome std::promise<std::string> p; // create a future to process the outcome std::future<std::string> f(p.get_future()); // start a thread using the promise to store the outcome std::thread t(doSomething, std::ref(p)); t.detach(); //... // process the outcome std::cout << "result: " << f.get() << std::endl; } catch (const std::exception& e) { std::cerr << "EXCEPTION: " << e.what() << std::endl; } catch (...) { std::cerr << "EXCEPTION " << std::endl; } }