分享一下在C++ Weekly学到的东西:std::async和std::future,本人在ubutun g++里调试过了。
通过调试我们对于std::async的第一个参数有更深刻的理解,代码如下:
#include <random>
#include <set>
#include <iostream>
#include <algorithm>
#include <future>
std::set<int> make_sorted_random(const size_t num_elems)
{
std::set<int> retval;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, num_elems-1);
std::generate_n(std::inserter(retval, retval.end()), num_elems, [&](){return dis(gen); });
return retval;
}
int main()
{
std::cout<<make_sorted_random(1000000).size()<<' '<<make_sorted_random(1000000).size<<'\n';
}
如图所示,在没有用到std::async的情况下,我们用的时间0:03.86 elapsed 99%CPU .
然后我们开始用std::async, 代码如下:
#include <random>
#include <set>
#include <iostream>
#include <algorithm>
#include <future>
std::set<int> make_sorted_random(const size_t num_elems)
{
std::set<int> retval;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, num_elems-1);
std::generate_n(std::inserter(retval, retval.end()), num_elems, [&](){return dis(gen); });
return retval;
}
int main()
{
std::cout<<std::async(make_sorted_random, 1000000).get().size()<<' '<<std::async(make_sorted_random, 1000000).get().size()<<'\n';
}
可以看到用了std::async的时候,程序消耗的时间并没有减少,反而略有增加。为什么呢?同时CPU利用也没有上去,说明还是一个CPU工作。也就是说上面代码的用法并没有起到多线程多核工作的用处。
最好我们看下面的代码:
#include <random>
#include <set>
#include <iostream>
#include <algorithm>
#include <future>
std::set<int> make_sorted_random(const size_t num_elems)
{
std::set<int> retval;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, num_elems-1);
std::generate_n(std::inserter(retval, retval.end()), num_elems, [&](){return dis(gen); });
return retval;
}
int main()
{
auto f1 = std::async(std::launch::async, make_sorted_random, 1000000);
auto f2 = std::async(std::launch::async, make_sorted_random, 1000000);
std::cout<<f1.get().size()<<' '<<f2.get().size()<<'\n';
}
这里我们用到了std::launch::async的policy, 这样程序就多线程多核运用了起来。
所以我们要深入std::async的第一个参数,去看看这些policy的用法,请参考:
https://en.cppreference.com/w/cpp/thread/async
在这里面有个例子对大家非常有帮助:
#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; // Calls (&x)->foo(42, "Hello") with default policy: // may print "Hello 42" concurrently or defer execution auto a1 = std::async(&X::foo, &x, 42, "Hello"); // Calls x.bar("world!") with deferred policy // prints "world!" when a2.get() or a2.wait() is called auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!"); // Calls X()(43); with async policy // prints "43" concurrently auto a3 = std::async(std::launch::async, X(), 43); a2.wait(); // prints "world!" std::cout << a3.get() << '\n'; // prints "53" } // if a1 is not done at this point, destructor of a1 prints "Hello 42" here
Possible output:
The sum is 10000 43 world! 53 Hello 42
从上面的例子中,我们可以看到std::launch::deferred的策略是让推迟线程的执行到调用wait的地方。