Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which std::async implementations use thread pools?

One of the advantages of using std::async instead of manually creating std::thread objects is supposed to be that std::async can use thread pools under the covers to avoid oversubscription problems. But which implementations do this? My understanding is that Microsoft's implementation does, but what about these other async implementations?

  • Gnu's libstdc++
  • LLVM's libc++
  • Just Software's library
  • Boost (for boost::thread::async, not std::async)

Thanks for any information you can offer.

like image 746
KnowItAllWannabe Avatar asked Mar 27 '13 18:03

KnowItAllWannabe


People also ask

Does STD async use a thread pool?

How does std::launch::async Work in Different Implementations? For now, we know that if no policy is specified, then std::async launches a callable function in a separate thread. However, the C++ standard does not specify whether the thread is a new one or reused from a thread pool.

What is asynchronous thread pool?

The thread-based asynchronous programming approach, also called “work-stealing” or “bulkheading”, allows one thread pool to hand over a task to another thread pool (let's call it a work thread pool) and be notified to handle the result when the worker thread pool is done with the task.

Why is thread pool used?

The thread pool is primarily used to reduce the number of application threads and provide management of the worker threads. Applications can queue work items, associate work with waitable handles, automatically queue based on a timer, and bind with I/O.

What is thread pool in C++?

Threadpool in C++ is basically a pool having a fixed number of threads used when we want to work multiple tasks together (run multiple threads concurrently). This thread sits idle in the thread pool when there are no tasks and when a task arrives, it is sent to the thread pool and gets assigned to the thread.


1 Answers

Black-Box Test

Though "white box" check can be done via inspecting boost, libstdc++ or libc++ sources, or checking documentation like just::thread or MSVC Concurrency Runtime, but I can not deny myself the pleasure of writing C++11 code!

I have made black-box test:

LIVE DEMO

#define BOOST_THREAD_PROVIDES_FUTURE #define BOOST_RESULT_OF_USE_DECLTYPE #include <boost/exception/exception.hpp> #include <boost/range/algorithm.hpp> #include <boost/move/iterator.hpp> #include <boost/phoenix.hpp> #include <boost/thread.hpp> #include <boost/config.hpp>  #include <unordered_set> #include <functional> #include <algorithm> #include <iterator> #include <iostream> #include <ostream> #include <cstddef> #include <string> #include <vector> #include <future> #include <thread> #include <chrono> #include <mutex>  // _____________________[CONFIGURATION]________________________ // namespace async_lib = std; const bool work_is_sleep = false; // ____________________________________________________________ //  using namespace std; using boost::phoenix::arg_names::arg1; using boost::thread_specific_ptr; using boost::back_move_inserter; using boost::type_name; using boost::count_if; using boost::copy;  template<typename Mutex> unique_lock<Mutex> locker(Mutex &m) {     return unique_lock<Mutex>(m); }  void do_work() {     if(work_is_sleep)     {         this_thread::sleep_for( chrono::milliseconds(20) );     }     else     {         volatile double result=0.0;         for(size_t i=0; i!=1<<22; ++i)             result+=0.1;     } }  int main() {     typedef thread::id TID;     typedef async_lib::future<TID> FTID;      unordered_set<TID> tids, live_tids;     vector<FTID> ftids;     vector<int> live_tids_count;     async_lib::mutex m;     generate_n     (         back_move_inserter(ftids), 64*thread::hardware_concurrency(),         [&]()         {             return async_lib::async([&]() -> TID             {                 static thread_specific_ptr<bool> fresh;                 if(fresh.get() == nullptr)                     fresh.reset(new bool(true));                 TID tid = this_thread::get_id();                 locker(m),                     live_tids.insert(tid),                     live_tids_count.push_back(int(live_tids.size()) * (*fresh ? -1 : 1));                 do_work();                 locker(m),                     live_tids.erase(tid);                 *fresh = false;                 return tid;             });         }     );     transform     (         begin(ftids), end(ftids),         inserter(tids, tids.end()),         [](FTID &x){return x.get();}     );      cout << "Compiler = " << BOOST_COMPILER                             << endl;     cout << "Standard library = " << BOOST_STDLIB                       << endl;     cout << "Boost = " << BOOST_LIB_VERSION                             << endl;     cout << "future type = " << type_name<FTID>()                       << endl;     cout << "Only sleep in do_work = " << boolalpha << work_is_sleep    << endl;     cout << string(32,'_')                                              << endl;     cout << "hardware_concurrency = " << thread::hardware_concurrency() << endl;     cout << "async count = " << ftids.size()                            << endl;     cout << "unique thread id's = " << tids.size()                      << endl;     cout << "live threads count (negative means fresh thread):"                ;     copy(live_tids_count, ostream_iterator<int>(cout," "));        cout << endl;     cout << "fresh count = " << count_if(live_tids_count, arg1 < 0)     << endl; } 

Compiler = Microsoft Visual C++ version 11.0 Standard library = Dinkumware standard library version 540 Boost = 1_53 future type = class std::future<class std::thread::id> Only sleep in do_work = false ________________________________ hardware_concurrency = 4 async count = 256 unique thread id's = 4 live threads count (negative means fresh thread):-1 -2 2 2 -3 -4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4  4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 3 4 4 4 4 4 4 4 4 4  4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4  4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 fresh count = 4 

Compiler = Microsoft Visual C++ version 11.0 Standard library = Dinkumware standard library version 540 Boost = 1_53 future type = class std::future<class std::thread::id> Only sleep in do_work = true ________________________________ hardware_concurrency = 4 async count = 256 unique thread id's = 34 live threads count (negative means fresh thread):-1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 18 18 18 18 18 18 18 18 18 18 18 18 18  18 18 18 18 -19 19 -20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 -21 21 21 -22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 - 23 23 23 23 -24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 -25 25 25 25 25 -26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 -27 27 27 27 27 27 -28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 -29 29 29 29 29 29 29 -30 30 30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 30 30 30 30 30 30 30 -31 31 31 31 31 31 31 31 -32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 -33 33  33 33 33 33 33 33 33 -34 34 34 34 34 34 34 34 34 11 12 11 12 13 14 15 16 15 10 11 12 13 14 fresh count = 34 

Compiler = Microsoft Visual C++ version 11.0 Standard library = Dinkumware standard library version 540 Boost = 1_53 future type = class boost::future<class std::thread::id> Only sleep in do_work = false ________________________________ hardware_concurrency = 4 async count = 256 unique thread id's = 256 live threads count (negative means fresh thread):-1 -2 -2 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -3 -4 -4 -4 -4 -4 -4 -4  -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -2 -3 -3 -3 -3 -4 -4 -4 -4 -2 -2 -2 -2 -3  -2 -2 -3 -1 -2 -2 -3 -3 -1 -2 -1 -2 -3 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3 -3 -2 -2 -2 -2 -3 -3 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2  -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3 -1 -2 -1 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3 -3 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2  -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3 -3 -2 -2 -3 -1 -2 -1 -2 -2 -2 -3 -2 -3 -1 -2 -2 -2 -3 -2 -3 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2  -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3 -3 -3 -2 -2 -2 -3 fresh count = 256 

Compiler = Microsoft Visual C++ version 11.0 Standard library = Dinkumware standard library version 540 Boost = 1_53 future type = class boost::future<class std::thread::id> Only sleep in do_work = true ________________________________ hardware_concurrency = 4 async count = 256 unique thread id's = 256 live threads count (negative means fresh thread):-1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -2 8 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102  -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132  -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162  -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192  -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222  -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252  -253 -254 -255 -256 fresh count = 256 

Compiler = GNU C++ version 4.8.0-alpha20121216 20121216 (experimental) Standard library = GNU libstdc++ version 20121216 Boost = 1_53 future type = std::future<std::thread::id> Only sleep in do_work = false ________________________________ hardware_concurrency = 4 async count = 256 unique thread id's = 1 live threads count (negative means fresh thread):-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 fresh count = 1 

Compiler = GNU C++ version 4.8.0-alpha20121216 20121216 (experimental) Standard library = GNU libstdc++ version 20121216 Boost = 1_53 future type = boost::future<std::thread::id> Only sleep in do_work = false ________________________________ hardware_concurrency = 4 async count = 256 unique thread id's = 122 live threads count (negative means fresh thread):-1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -20 -21 -22 -23 -24 -25 -25 -26 -27 -28 -29 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -59 -60 -61 -62 -63 -64 -64 -64 -63 -61 -55 -48 -41 -33 -34 -27 -27 -28 -29 -29 -30 -31 -31 -30 -28 -29 -28 -28 -28 -29 -30 -30 -31 -30 -31 -31 -31 -32 -31 -26 -24 -25 -26 -25 -23 -23 -22 -20 -21 -19 -20 -20 -19 -20 -21 -21 -22 -21 -22 -23 -24 -24 -25 -26 -27 -25 -25 -25 -22 -23 -22 -23 -23 -24 -25 -26 -27 -27 -28 -29 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -51 -52 -53 -54 -55 -56 -57 -56 -54 -3 -3 -4 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -55 -56 -57 -58 -59 -60 -61 -61 -61 -62 -63 -64 -65 -64 -62 -63 -63 -61 -62 -61 -59 -60 -59 -57 -55 -56 fresh count = 256 

Compiler = Clang version 3.2 (tags/RELEASE_32/final) Standard library = libc++ version 1101 Boost = 1_53 future type = NSt3__16futureINS_11__thread_idEEE Only sleep in do_work = false ________________________________ hardware_concurrency = 4 async count = 256 unique thread id's = 255 live threads count (negative means fresh thread):-1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -121 -122 -122 -123 -123 -124 -125 -126 -127 -126 -127 -128 -129 -129 -128 -129 -128 -129 -129 -128 -129 -129 -128 -126 -127 -128 -128 -129 -129 -130 -129 -129 -128 -129 -129 -130 -131 -132 -133 -134 -135 -136 -134 -132 -133 -134 -134 -133 -132 -133 -132 -133 -134 -133 -131 -129 -127 -124 -125 -121 -119 -120 -118 -119 -118 -117 -115 -111 -107 -105 -106 -103 -100 -97 -95 -96 -94 -90 -87 -81 -73 -74 -71 -72 -73 -74 -75 -70 -71 -66 -60 -59 -60 -61 -62 -63 -64 -61 -58 -55 -55 -52 -53 -54 -54 -55 -56 -56 -57 -54 -55 -56 -56 -57 -57 -58 -56 -54 -55 -56 -56 -57 -58 -58 -59 -58 -58 -58 -58 -59 -60 fresh count = 256 

As can be seen - MSVC does reuse threads, i.e. it is very likely that it's scheme is some kind of thread pool.

like image 177
Evgeny Panasyuk Avatar answered Sep 24 '22 23:09

Evgeny Panasyuk