1. 线程池原理 我们使用线程的时候就去创建一个线程,这样实现起来非常简便 但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用: 执行完一个任务,并不被销毁,而是可以继续执行其他的任务呢?
线程池是一种多线程处理形式,处理过程中将任务添加到队列 在创建线程后自动启动
这些任务。线程池线程都是后台线程。 每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。 如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。 如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。 超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
在各个编程语言的语种中都有线程池的概念,并且很多语言中直接提供了线程池,作为程序猿直接使用就可以了,下面给大家介绍一下线程池的实现原理:
2. C实现线程池 2.1 头文件 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 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 #pragma once #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <string.h> #include <unistd.h> #define NUMBER 2 typedef struct Task { void (*function)(void * args); void * args; }Task; typedef struct ThreadPool { Task* task_queue; int queue_capacity; int queue_size; int queue_front; int queue_tail; pthread_t managerID; pthread_t * threadID; int min_num; int max_num; int busy_num; int live_num; int exit_num; pthread_mutex_t mutex_pool; pthread_mutex_t mutex_busy_num; pthread_cond_t producer_wait_consumer; pthread_cond_t consumer_wait_producer; int shutdown; }ThreadPool; ThreadPool* Thread_Pool_Create (int min, int max, int queuesize) ; int Thread_Pool_Destroy (ThreadPool* pool) ;void Thread_Pool_Add (ThreadPool* pool, void (*function)(void *), void * args) ;int Get_Busy_Num (ThreadPool* pool) ;int Get_Live_Num (ThreadPool* pool) ;void * Worker (void * args) ;void * Manager (void * args) ;void Thread_Exit (ThreadPool* pool) ;
2.2 源文件的定义 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 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 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 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 #include "threadpool.h" ThreadPool* Thread_Pool_Create (int min, int max, int queuesize) { ThreadPool* pool = (ThreadPool*)malloc (sizeof (ThreadPool)); do { if (pool == NULL ) { printf ("malloc threadpool fail\n" ); break ; } pool->threadID = (pthread_t *)malloc (sizeof (pthread_t ) * max); if (pool->threadID == NULL ) { printf ("malloc threadID fail\n" ); break ; } memset (pool->threadID, 0 , sizeof (pthread_t ) * max); pool->max_num = max; pool->min_num = min; pool->busy_num = 0 ; pool->live_num = min; pool->exit_num = 0 ; if (pthread_mutex_init(&pool->mutex_pool, NULL ) != 0 || pthread_mutex_init(&pool->mutex_busy_num, NULL ) != 0 || pthread_cond_init(&pool->producer_wait_consumer, NULL ) != 0 || pthread_cond_init(&pool->consumer_wait_producer, NULL ) != 0 ) { printf ("mutex or cond init fail\n" ); break ; } pool->task_queue = (Task*)malloc (sizeof (Task) * queuesize); if (pool->task_queue == NULL ) { printf ("malloc task_queue fail...\n" ); break ; } pool->queue_capacity = queuesize; pool->queue_size = 0 ; pool->queue_front = 0 ; pool->queue_tail = 0 ; pool->shutdown = 0 ; pthread_create(&pool->managerID, NULL , Manager, pool); for (int i = 0 ; i < min; ++i) { pthread_create(&pool->threadID[i], NULL , Worker, pool); } return pool; } while (0 ); if (pool && pool->threadID) free (pool->threadID); if (pool) free (pool); return NULL ; } int Thread_Pool_Destroy (ThreadPool* pool) { if (pool == NULL ) return -1 ; pool->shutdown = 1 ; pthread_join(pool->managerID, NULL ); for (int i = 0 ; i < pool->live_num; ++i) pthread_cond_signal(&pool->consumer_wait_producer); if (pool->task_queue) free (pool->task_queue); if (pool->threadID) free (pool->threadID); pthread_mutex_destroy(&pool->mutex_pool); pthread_mutex_destroy(&pool->mutex_busy_num); pthread_cond_destroy(&pool->consumer_wait_producer); pthread_cond_destroy(&pool->producer_wait_consumer); free (pool); pool = NULL ; return 0 ; } void Thread_Pool_Add (ThreadPool* pool, void (*function)(void *), void * args) { pthread_mutex_lock(&pool->mutex_pool); while (pool->queue_size == pool->queue_capacity && !pool->shutdown) { pthread_cond_wait(&pool->producer_wait_consumer, &pool->mutex_pool); } if (pool->shutdown) { pthread_mutex_unlock(&pool->mutex_pool); return ; } pool->task_queue[pool->queue_tail].function = function; pool->task_queue[pool->queue_tail].args = args; pool->queue_tail = (pool->queue_tail + 1 ) % pool->queue_capacity; pool->queue_size++; pthread_cond_signal(&pool->consumer_wait_producer); pthread_mutex_unlock(&pool->mutex_pool); } int Get_Busy_Num (ThreadPool* pool) { pthread_mutex_lock(&pool->mutex_busy_num); int busynum = pool->busy_num; pthread_mutex_unlock(&pool->mutex_busy_num); return busynum; } int Get_Live_Num (ThreadPool* pool) { pthread_mutex_lock(&pool->mutex_pool); int livenum = pool->live_num; pthread_mutex_unlock(&pool->mutex_pool); return livenum; } void * Worker (void * args) { ThreadPool* pool = (ThreadPool*)args; while (1 ) { pthread_mutex_lock(&pool->mutex_pool); while (pool->queue_size == 0 && !pool->shutdown) { pthread_cond_wait(&pool->consumer_wait_producer,&pool->mutex_pool); if (pool->exit_num > 0 ) { pool->exit_num--; if (pool->live_num > pool->min_num) { pool->live_num--; pthread_mutex_unlock(&pool->mutex_pool); Thread_Exit(pool); } } } if (pool->shutdown) { pthread_mutex_unlock(&pool->mutex_pool); Thread_Exit(pool); } Task task; task.function = pool->task_queue[pool->queue_front].function; task.args = pool->task_queue[pool->queue_front].args; pool->queue_front = (pool->queue_front + 1 ) % pool->queue_capacity; pool->queue_size--; pthread_cond_signal(&pool->producer_wait_consumer); pthread_mutex_unlock(&pool->mutex_pool); printf ("thread %ld start working...\n" , pthread_self()); pthread_mutex_lock(&pool->mutex_busy_num); pool->busy_num++; pthread_mutex_unlock(&pool->mutex_busy_num); task.function(task.args); free (task.args); task.args = NULL ; printf ("thread %ld end working\n" , pthread_self()); pthread_mutex_lock(&pool->mutex_busy_num); pool->busy_num--; pthread_mutex_unlock(&pool->mutex_busy_num); if (pool->shutdown) Thread_Exit(pool); } return NULL ; } void * Manager (void * args) { ThreadPool* pool = (ThreadPool*)args; while (!pool->shutdown) { sleep(3 ); pthread_mutex_lock(&pool->mutex_pool); int queue_size = pool->queue_size; int live_num = pool->live_num; pthread_mutex_unlock(&pool->mutex_pool); pthread_mutex_lock(&pool->mutex_busy_num); int busy_num = pool->busy_num; pthread_mutex_unlock(&pool->mutex_busy_num); if (queue_size > live_num && live_num < pool->max_num) { pthread_mutex_lock(&pool->mutex_pool); int counter = 0 ; for (int i = 0 ; i < pool->max_num && counter < NUMBER && pool->live_num < pool->max_num; ++i) { if (pool->threadID[i] == 0 ) { pthread_create(&pool->threadID[i], NULL , Worker, pool); counter++; pool->live_num++; } } pthread_mutex_unlock(&pool->mutex_pool); } if (busy_num * 2 < live_num && live_num > pool->min_num) { pthread_mutex_lock(&pool->mutex_pool); pool->exit_num = NUMBER; pthread_mutex_unlock(&pool->mutex_pool); for (int i = 0 ; i < NUMBER; ++i) { pthread_cond_signal(&pool->consumer_wait_producer); } } } return NULL ; } void Thread_Exit (ThreadPool* pool) { pthread_t tid = pthread_self(); for (int i = 0 ; i < pool->max_num; ++i) { if (pool->threadID[i] == tid) { pool->threadID[i] = 0 ; printf ("thread %ld exiting...\n" , tid); break ; } } pthread_exit(NULL ); }
2.3 测试部分 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 #include "threadpool.h" void Task_Test (void * args) { int num = *(int *)args; printf ("thread %ld is working,number = %d\n" , pthread_self(), num); sleep(1 ); return ; } int main () { ThreadPool* pool = Thread_Pool_Create(3 , 10 , 100 ); for (int i = 0 ; i < 100 ; ++i) { int * num = (int *)malloc (sizeof (int )); *num = i + 100 ; Thread_Pool_Add(pool, Task_Test, num); } sleep(30 ); Thread_Pool_Destroy(pool); return 0 ; }
3. C++实现线程池 3.1 头文件 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 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 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 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 #define _CRT_SECURE_NO_WARNINGS #pragma once #include <iostream> #include <string.h> #include <string> #include <pthread.h> #include <stdlib.h> #include <queue> #include <unistd.h> using namespace std;using callback = void (*)(void *);template <typename T>struct Task { Task () { function = nullptr ; args = nullptr ; } Task (callback fun, void * args) { function = fun; this -> args = (T*)args; } callback function; T* args; }; template <typename T>class TaskQueue { public : TaskQueue () { pthread_mutex_init (&mutex,NULL ); } ~TaskQueue () { pthread_mutex_destroy (&mutex); } void AddTask (Task<T> task) { pthread_mutex_lock (&mutex); queue.push (task); pthread_mutex_unlock (&mutex); } void AddTask (callback fun, void * args) { pthread_mutex_lock (&mutex); Task<T> task (fun,args) ; queue.push (task); pthread_mutex_unlock (&mutex); } Task<T> TakeTask () { Task<T> task; pthread_mutex_lock (&mutex); if (queue.size () > 0 ) { task = queue.front (); queue.pop (); } pthread_mutex_unlock (&mutex); return task; } inline int GetTaskNum () { return queue.size (); } private : pthread_mutex_t mutex; std::queue<Task<T>> queue; }; template <typename T>class ThreadPool { public : ThreadPool (int min , int max) { taskqueue = new TaskQueue<T>; min_num = min; max_num = max; busy_num = 0 ; live_num = min; threadID = new pthread_t [max]; if (threadID == nullptr ) { cout << "new threadID fail" << endl; } memset (threadID, 0 , sizeof (pthread_t ) * max); if (pthread_mutex_init (&mutex_pool, NULL ) != 0 || pthread_cond_init (¬empty, NULL ) != 0 ) { cout << "mutex or cond init fail" << endl; } for (size_t i = 0 ; i < min; ++i) { pthread_create (&threadID[i], NULL , Work, this ); cout << "create thread ID :" << to_string (threadID[i]) << endl; } pthread_create (&managerID, NULL , Manage, this ); } ~ThreadPool () { shutdown = true ; pthread_join (managerID, NULL ); for (int i = 0 ; i < live_num; ++i) { pthread_cond_signal (¬empty); } if (taskqueue) { delete taskqueue; } if (threadID) { delete [] threadID; } pthread_mutex_destroy (&mutex_pool); pthread_cond_destroy (¬empty); } void Add_Task (Task<T> task) { if (shutdown) return ; taskqueue->AddTask (task); pthread_cond_signal (¬empty); } int Get_Busy_Num () { int busynum = 0 ; pthread_mutex_lock (&mutex_pool); busynum = busy_num; pthread_mutex_unlock (&mutex_pool); return busynum; } int Get_Live_Num () { int livenum = 0 ; pthread_mutex_lock (&mutex_pool); livenum = live_num; pthread_mutex_unlock (&mutex_pool); return livenum; } private : static void * Work (void * args) { ThreadPool* pool = static_cast <ThreadPool*>(args); while (true ) { pthread_mutex_lock (&pool->mutex_pool); while (pool->taskqueue->GetTaskNum () == 0 && !pool->shutdown) { cout << "thread :" << to_string (pthread_self ()) << " waiting..." << endl; pthread_cond_wait (&pool->notempty, &pool->mutex_pool); if (pool->exit_num > 0 ) { pool->exit_num--; if (pool->live_num > pool->min_num) { pool->live_num--; pthread_mutex_unlock (&pool->mutex_pool); pool->Thread_Exit (); } } } if (pool->shutdown) { pthread_mutex_unlock (&pool->mutex_pool); pool->Thread_Exit (); } Task<T> task = pool->taskqueue->TakeTask (); pool->busy_num++; pthread_mutex_unlock (&pool->mutex_pool); cout << "thread :" << to_string (pthread_self ()) << " start working..." << endl; task.function (task.args); delete task.args; task.args = nullptr ; cout << "thread :" << to_string (pthread_self ()) << " end working..." << endl; pthread_mutex_lock (&pool->mutex_pool); pool->busy_num--; pthread_mutex_unlock (&pool->mutex_pool); } return nullptr ; } static void * Manage (void * args) { ThreadPool* pool = static_cast <ThreadPool*>(args); while (!pool->shutdown) { sleep (5 ); pthread_mutex_lock (&pool->mutex_pool); int livenum = pool->live_num; int busynum = pool->busy_num; int queuesize = pool->taskqueue->GetTaskNum (); pthread_mutex_unlock (&pool->mutex_pool); const int NUMBER = 2 ; if (queuesize > livenum && livenum < pool->max_num) { pthread_mutex_lock (&pool->mutex_pool); int num = 0 ; for (int i = 0 ; i < pool->max_num && num < NUMBER && pool->live_num < pool->max_num ; ++i) { if (pool->threadID[i] == 0 ) { pthread_create (&pool->threadID[i], NULL , Work, pool); num++; pool->live_num++; } } pthread_mutex_unlock (&pool->mutex_pool); } if (busynum * 2 < livenum && livenum > pool->min_num) { pthread_mutex_lock (&pool->mutex_pool); pool->exit_num = NUMBER; pthread_mutex_unlock (&pool->mutex_pool); for (int i = 0 ; i < NUMBER; ++i) { pthread_cond_signal (&pool->notempty); } } } return nullptr ; } void Thread_Exit () { pthread_t tid = pthread_self (); for (int i = 0 ; i < max_num; ++i) { if (threadID[i] == tid) { cout << "thread :" << to_string (pthread_self ()) << "exiting" << endl; threadID[i] = 0 ; break ; } } pthread_exit (NULL ); } private : pthread_mutex_t mutex_pool; pthread_cond_t notempty; pthread_t * threadID; pthread_t managerID; TaskQueue<T>* taskqueue; int min_num; int max_num; int busy_num; int live_num; int exit_num; bool shutdown = false ; };
3.2 测试部分 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include "ThreadPool.h" void Task_Test (void * args) { int num = *(int *)args; cout<<"thread :" << pthread_self () << " is working " << "number =" << num <<endl; sleep (1 ); return ; } int main () { ThreadPool<int > pool (3 , 10 ) ; for (int i = 0 ; i < 100 ; ++i) { int * num = new int (i+100 ); pool.Add_Task (Task <int >(Task_Test,num)); } sleep (40 ); return 0 ; }
以上只是基于C修改出对应于C++的代码
并且以上代码存在一个问题 输出的结果有时会因为线程原因出现混乱 可以通过加锁来解决,但锁的数量超过1就容易导致死锁问题,所以暂且搁置
4. C++11实现线程池 并非原创,摘于此处
4.1 头文件 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 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 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 #pragma once #include <queue> #include <thread> #include <condition_variable> #include <atomic> #include <stdexcept> #include <future> #include <vector> #include <functional> namespace std{ #define THREADPOOL_MAX_NUM 16 class threadpool { unsigned short _initsize; using Task = function<void ()>; vector<thread> _pool; queue<Task> _tasks; mutex _lock; mutex _lockGrow; condition_variable _task_cv; atomic<bool > _run{true }; atomic<int > _spa_trd_num{0 }; public : inline threadpool (unsigned short size = 4 ) { _initsize = size; Add_Thread (size); } inline ~threadpool () { _run = false ; _task_cv.notify_all (); for (thread& thread : _pool) { if (thread.joinable ()) thread.join (); } } template <typename F,typename ... Args> auto commit (F&& f, Args&& ...args) -> future<decltype (f(args...)) > { if (!_run) throw runtime_error{"commit auto stop" }; using RetType = decltype (f (args...)); auto task = make_shared<packaged_task<RetType ()>>(bind (forward<F>(f), forward<Args>(args)...)); future<RetType> future = task->get_future (); { lock_guard<mutex> lock{_lock}; _tasks.emplace ([task]() {(*task)(); }); } if (_spa_trd_num < 1 && _pool.size () < THREADPOOL_MAX_NUM) Add_Thread (1 ); _task_cv.notify_one (); return future; } template <typename F> void commit2 (F&& f) { if (!_run) return ; { lock_guard<mutex> lock{_lock}; _tasks.emplace (forward<F>(f)); } if (_spa_trd_num < 1 && _pool.size () < THREADPOOL_MAX_NUM) Add_Thread (1 ); _task_cv.notify_one (); } int idlCount () { return _spa_trd_num; } int thrCount () { return _pool.size (); } private : void Add_Thread (unsigned short size) { if (!_run) throw runtime_error{"Add_Thread stop" }; unique_lock<mutex> lockgrow{_lockGrow}; for (; _pool.size () < THREADPOOL_MAX_NUM && size > 0 ; --size) { _pool.emplace_back ([this ] { while (true ) { Task task; { unique_lock<mutex> lock{_lock}; _task_cv.wait (lock, [this ] {return !_run || !_tasks.empty (); }); if (!_run && _tasks.empty ()) return ; _spa_trd_num--; task = move (_tasks.front ()); _tasks.pop (); } task (); if (_spa_trd_num > 0 && _pool.size () > _initsize) return ; { unique_lock<mutex> lock{_lock}; _spa_trd_num++; } } }); { unique_lock<mutex> lock{_lock}; _spa_trd_num++; } } } }; }
要使用pthread依赖库
4.2 测试部分 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 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 70 71 72 73 74 75 76 77 #include "ThreadPool.hpp" #include <iostream> void fun1 (int slp) { printf ("fun1 %ld\n" , std::this_thread::get_id ()); if (slp > 0 ) { printf ("fun1 sleep %ld ========= %ld\n" , slp, std::this_thread::get_id ()); std::this_thread::sleep_for (std::chrono::milliseconds (slp)); } } struct gfun { int operator () (int n) { printf ("gfun %ld\n" , n, std::this_thread::get_id ()); return 42 ; } }; class A { public : static int Afun (int n = 0 ) { std::cout << n << "Afun " << std::this_thread::get_id () << std::endl; return n; } static std::string Bfun (int n, std::string str, char c) { std::cout << n << "Bfun " << str.c_str () << " " << (int )c << " " << std::this_thread::get_id () << std::endl; return str; } }; int main () try { std::threadpool executor{ 50 }; std::future<void > ff = executor.commit (fun1, 0 ); std::future<int > fg = executor.commit (gfun{}, 0 ); std::future<std::string> gh = executor.commit (A::Bfun, 9998 , "mult args" , 123 ); std::future<std::string> fh = executor.commit ([]()->std::string { std::cout << "hello, fh ! " << std::this_thread::get_id () << std::endl; return "hello,fh ret !\n" ; }); std::this_thread::sleep_for (std::chrono::seconds (1 )); std::cout << fg.get () << " " << fh.get ().c_str () << " " << std::this_thread::get_id () << std::endl; std::cout << " ======= fun1,55 ========= " << std::this_thread::get_id () << std::endl; executor.commit (fun1, 55 ).get (); std::threadpool pool (4 ) ; std::vector< std::future<int > > results; for (int i = 0 ; i < 8 ; ++i) { results.emplace_back ( pool.commit ([i] { std::cout << "hello " << i << std::endl; std::this_thread::sleep_for (std::chrono::seconds (3 )); std::cout << "world " << i << std::endl; return i * i; }) ); } std::this_thread::sleep_for (std::chrono::seconds (15 )); for (auto && result : results) std::cout << result.get () << ' ' ; std::cout << std::endl; return 0 ; } catch (std::exception& e){ std::cout << "some error " << std::this_thread::get_id () << e.what () << std::endl; }
测试结果