我在上篇博客C++不是C/C++ 提到C++的std::function給C++帶來了閉包。有人留言說:“沒有閉包真的很痛苦嗎?不見得吧!”。我的回答是:“是的,真的很痛苦。”
首先來看閉包的兩個作用:1.打包上下文 2.延遲調用。我們來設計一個linux 線程池來說明閉包的價值。
linux上線程創建的接口是:
int pthread_create(pthread_t*, const pthread_attr_t*, void *(*)(void *), void *params);
后面兩個參數分別是一個函數指針,以及傳給該函數的參數打包結構體的指針。上下文只能通過params結構體打包傳入,以下代碼只做使用說明,不保證正確性:
struct Params { int a; double b; }; void threadFunc(void* p) { struct Params* params = (struct Params*)(p); ...... } int f() { int a = 1; double b = 2; Params params; params.a = 1; params.b = 2; pthread_create(&id, NULL, (void *)threadFunc, ¶ms); }
線程執行的函數需要在內部將傳入的params強制轉換成真正的類型,才能獲取所需要的參數。
假設我們設計一個C的線程池,那么,我無法預計扔到線程池里面的函數是什么類型的,也不知道它的參數是什么結構的,所以接口設計也需要同pthread_create一樣,傳入一個void*(*)(void*)的函數指
針以及一個void* 類型的參數打包。接口可能設計如下:
void schedule(struct ThreadPool* threadPool, void *(*)(void *)f, void *params)
好了,假設我們程序中有個函數
void Max(int a, int b, int* z) { *z = a > b? a : b; }
一個數組a[10000], 數組b[10000] ,數組c[i], 我要取得a[i] b[i]中較大者, 放入到c[i],代碼可能如下(表明用法,不保證正確性):
struct MaxParam { int a; int b; int* c; }; void Max2(void* p) { struct MaxParam* param = (struct MaxParam*)(p); Max(param->a, param->b, param->c); } void g() { int a[10000]; int b[10000]; int c[10000]; for (int i = 0; i < 10000; ++i) { MaxParam p; p.a = a[i]; p.b = b[i];
p.c = &c[i];
schedule(&threadPool, (void *)Max2, &a); // 扔到線程池
}
wait(&threadPool); // 等待所有線程執行完成
}
可以看出,要重新定義一個新函數Max2和新的結構體MaxParam來配合線程池的使用, 代碼中可能有千千萬萬的函數需要扔到線程池中執行,就要重新弄出千千萬萬個配合的函數和配合的結構體,
麻煩之極啊!
C++有了閉包會怎么做呢?接口就設計如下了:
class ThreadPool { void schedule(std::function<void (void)> func); void wait(); };
接口簡單清晰,使用起來方便極了,(代碼只表示用法,不保證正確性):
std::vector<int> x; std::vector<int> y; std::vector<int> z; for (int i = 0; i < 10000; ++i) { pool.schedule( std::bind(&Max, x[i], y[i], &z[i])); } pool.wait();
不用打包參數,也不需要搞個新函數配合線程池,用起來想出錯真的很難啊!
C++一些復雜的設計是為了更加的簡單!
可以看看boost threadpool 設計http://threadpool.sourceforge.net/,我寫了一個例子如下
https://github.com/egametang/Egametang/blob/master/Cpp/Platform/Thread/ThreadPoolTest.cc