轉 c++多線程編程

c++多線程編程html

一直對多線程編程這一塊很陌生,決定花一點時間整理一下。ios

os:ubuntu 10.04  c++c++

1.最基礎,進程同時建立5個線程,各自調用同一個函數sql

 

 1 #include <iostream>  
 2 #include <pthread.h> //多線程相關操做頭文件,可移植衆多平臺  
 3   
 4 using namespace std;  
 5   
 6 #define NUM_THREADS 5 //線程數  
 7   
 8 void* say_hello( void* args )  
 9 {  
10     cout << "hello..." << endl;  
11 } //函數返回的是函數指針,便於後面做爲參數  
12   
13 int main()  
14 {  
15     pthread_t tids[NUM_THREADS]; //線程id  
16     for( int i = 0; i < NUM_THREADS; ++i )  
17     {  
18         int ret = pthread_create( &tids[i], NULL, say_hello, NULL ); //參數:建立的線程id,線程參數,線程運行函數的起始地址,運行函數的參數  
19         if( ret != 0 ) //建立線程成功返回0  
20         {  
21             cout << "pthread_create error:error_code=" << ret << endl;  
22         }  
23     }  
24     pthread_exit( NULL ); //等待各個線程退出後,進程才結束,不然進程強制結束,線程處於未終止的狀態  
25 }  

 

輸入命令:g++ -o muti_thread_test_1 muti_thread_test_1.cpp -lpthread數據庫

 

注意:編程

1)此爲c++程序,故用g++來編譯生成可執行文件,而且要調用處理多線程操做相關的靜態連接庫文件pthread。ubuntu

2)-lpthread 編譯選項到位置可任意,如g++ -lpthread -o muti_thread_test_1 muti_thread_test_1.cppcentos

3)注意gcc和g++的區別,轉到此文:點擊打開連接數組

測試結果:緩存

1 wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_1  
2 hello...hello...  
3 hello...  
4 hello...  
5   
6 hello...  

 

1 wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_1  
2 hello...hello...hello...  
3   
4 hello...  
5 hello... 

 

可知,兩次運行的結果會有差異,這不是多線程的特色吧?這顯然沒有同步?還有待進一步探索...

多線程的運行是混亂的,混亂就是正常?

 

2.線程調用到函數在一個類中,那必須將該函數聲明爲靜態函數函數

由於靜態成員函數屬於靜態全局區,線程能夠共享這個區域,故能夠各自調用。

 

 1 #include <iostream>  
 2 #include <pthread.h>  
 3   
 4 using namespace std;  
 5   
 6 #define NUM_THREADS 5  
 7   
 8 class Hello  
 9 {  
10 public:  
11     static void* say_hello( void* args )  
12     {  
13         cout << "hello..." << endl;  
14     }  
15 };  
16   
17 int main()  
18 {  
19     pthread_t tids[NUM_THREADS];  
20     for( int i = 0; i < NUM_THREADS; ++i )  
21     {  
22         int ret = pthread_create( &tids[i], NULL, Hello::say_hello, NULL );  
23         if( ret != 0 )  
24         {  
25             cout << "pthread_create error:error_code" << ret << endl;  
26         }  
27     }  
28     pthread_exit( NULL );  
29 }  

 

測試結果:

 

1 wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_2  
2 hello...  
3 hello...  
4 hello...  
5 hello...  
6 hello...  

 

1 wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_2  
2 hello...hello...hello...  
3   
4   
5 hello...  
6 hello...

 

3.如何在線程調用函數時傳入參數呢?

先看下面修改的代碼,傳入線程編號做爲參數:

 

 1 #include <iostream>  
 2 #include <pthread.h> //多線程相關操做頭文件,可移植衆多平臺  
 3   
 4 using namespace std;  
 5   
 6 #define NUM_THREADS 5 //線程數  
 7   
 8 void* say_hello( void* args )  
 9 {  
10     int i = *( (int*)args ); //對傳入的參數進行強制類型轉換,由無類型指針轉變爲整形指針,再用*讀取其指向到內容  
11     cout << "hello in " << i <<  endl;  
12 } //函數返回的是函數指針,便於後面做爲參數  
13   
14 int main()  
15 {  
16     pthread_t tids[NUM_THREADS]; //線程id  
17     cout << "hello in main.." << endl;  
18     for( int i = 0; i < NUM_THREADS; ++i )  
19     {  
20         int ret = pthread_create( &tids[i], NULL, say_hello, (void*)&i ); //傳入到參數必須強轉爲void*類型,即無類型指針,&i表示取i的地址,即指向i的指針  
21         cout << "Current pthread id = " << tids[i] << endl; //用tids數組打印建立的進程id信息  
22         if( ret != 0 ) //建立線程成功返回0  
23         {  
24             cout << "pthread_create error:error_code=" << ret << endl;  
25         }  
26     }  
27     pthread_exit( NULL ); //等待各個線程退出後,進程才結束,不然進程強制結束,線程處於未終止的狀態  
28 }  

 

測試結果:

 1 wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_3  
 2 hello in main..  
 3 Current pthread id = 3078458224  
 4 Current pthread id = 3070065520  
 5 hello in hello in 2  
 6 1  
 7 Current pthread id = hello in 2  
 8 3061672816  
 9 Current pthread id = 3053280112  
10 hello in 4  
11 Current pthread id = hello in 4  
12 3044887408 

 

顯然不是想要的結果,調用順序很亂,這是爲何呢?

 

這是由於多線程到緣故,主進程還沒開始對i賦值,線程已經開始跑了...?

修改代碼以下:

 

 1 #include <iostream>  
 2 #include <pthread.h> //多線程相關操做頭文件,可移植衆多平臺  
 3   
 4 using namespace std;  
 5   
 6 #define NUM_THREADS 5 //線程數  
 7   
 8 void* say_hello( void* args )  
 9 {  
10     cout << "hello in thread " << *( (int *)args ) <<  endl;  
11 } //函數返回的是函數指針,便於後面做爲參數  
12   
13 int main()  
14 {  
15     pthread_t tids[NUM_THREADS]; //線程id  
16     int indexes[NUM_THREADS]; //用來保存i的值避免被修改  
17   
18     for( int i = 0; i < NUM_THREADS; ++i )  
19     {  
20         indexes[i] = i;  
21         int ret = pthread_create( &tids[i], NULL, say_hello, (void*)&(indexes[i]) );  
22         if( ret != 0 ) //建立線程成功返回0  
23         {  
24             cout << "pthread_create error:error_code=" << ret << endl;  
25         }  
26     }  
27     for( int i = 0; i < NUM_THREADS; ++i )  
28         pthread_join( tids[i], NULL ); //pthread_join用來等待一個線程的結束,是一個線程阻塞的函數  
29 }  

 

測試結果:

 

1 wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_3  
2 hello in thread hello in thread hello in thread hello in thread hello in thread 30124  

 

這是正常的嗎?感受仍是有問題...待續

代碼中若是沒有pthread_join主線程會很快結束從而使整個進程結束,從而使建立的線程沒有機會開始執行就結束了。加入pthread_join後,主線程會一直等待直到等待的線程結束本身才結束,使建立的線程有機會執行。

 

 

4.線程建立時屬性參數的設置pthread_attr_t及join功能的使用

線程的屬性由結構體pthread_attr_t進行管理。

typedef struct
{
    int                           detachstate;     線程的分離狀態
    int                          schedpolicy;   線程調度策略
    struct sched_param      schedparam;   線程的調度參數
    int inheritsched; 線程的繼承性 
    int scope; 線程的做用域 
    size_t guardsize; 線程棧末尾的警惕緩衝區大小 
    int stackaddr_set; void * stackaddr; 線程棧的位置 
    size_t stacksize; 線程棧的大小
}pthread_attr_t;

 

 1 #include <iostream>  
 2 #include <pthread.h>  
 3   
 4 using namespace std;  
 5   
 6 #define NUM_THREADS 5  
 7   
 8 void* say_hello( void* args )  
 9 {  
10     cout << "hello in thread " << *(( int * )args) << endl;  
11     int status = 10 + *(( int * )args); //線程退出時添加退出的信息,status供主程序提取該線程的結束信息  
12     pthread_exit( ( void* )status );   
13 }  
14   
15 int main()  
16 {  
17     pthread_t tids[NUM_THREADS];  
18     int indexes[NUM_THREADS];  
19       
20     pthread_attr_t attr; //線程屬性結構體,建立線程時加入的參數  
21     pthread_attr_init( &attr ); //初始化  
22     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); //是設置你想要指定線程屬性參數,這個參數代表這個線程是能夠join鏈接的,join功能表示主程序能夠等線程結束後再去作某事,實現了主程序和線程同步功能  
23     for( int i = 0; i < NUM_THREADS; ++i )  
24     {  
25         indexes[i] = i;  
26         int ret = pthread_create( &tids[i], &attr, say_hello, ( void* )&( indexes[i] ) );  
27         if( ret != 0 )  
28         {  
29         cout << "pthread_create error:error_code=" << ret << endl;  
30     }  
31     }   
32     pthread_attr_destroy( &attr ); //釋放內存   
33     void *status;  
34     for( int i = 0; i < NUM_THREADS; ++i )  
35     {  
36     int ret = pthread_join( tids[i], &status ); //主程序join每一個線程後取得每一個線程的退出信息status  
37     if( ret != 0 )  
38     {  
39         cout << "pthread_join error:error_code=" << ret << endl;  
40     }  
41     else  
42     {  
43         cout << "pthread_join get status:" << (long)status << endl;  
44     }  
45     }  
46 }  

 

 

測試結果:

 

 1 wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_4  
 2 hello in thread hello in thread hello in thread hello in thread 0hello in thread 321  
 3   
 4   
 5   
 6 4  
 7 pthread_join get status:10  
 8 pthread_join get status:11  
 9 pthread_join get status:12  
10 pthread_join get status:13  
11 pthread_join get status:14  

 

5.互斥鎖的實現
互斥鎖是實現線程同步的一種機制,只要在臨界區先後對資源加鎖就能阻塞其餘進程的訪問。

 

 1 #include <iostream>  
 2 #include <pthread.h>  
 3   
 4 using namespace std;  
 5   
 6 #define NUM_THREADS 5  
 7   
 8 int sum = 0; //定義全局變量,讓全部線程同時寫,這樣就須要鎖機制  
 9 pthread_mutex_t sum_mutex; //互斥鎖  
10   
11 void* say_hello( void* args )  
12 {  
13     cout << "hello in thread " << *(( int * )args) << endl;  
14     pthread_mutex_lock( &sum_mutex ); //先加鎖,再修改sum的值,鎖被佔用就阻塞,直到拿到鎖再修改sum;  
15     cout << "before sum is " << sum << " in thread " << *( ( int* )args ) << endl;  
16     sum += *( ( int* )args );  
17     cout << "after sum is " << sum << " in thread " << *( ( int* )args ) << endl;  
18     pthread_mutex_unlock( &sum_mutex ); //釋放鎖,供其餘線程使用  
19     pthread_exit( 0 );   
20 }  
21   
22 int main()  
23 {  
24     pthread_t tids[NUM_THREADS];  
25     int indexes[NUM_THREADS];  
26       
27     pthread_attr_t attr; //線程屬性結構體,建立線程時加入的參數  
28     pthread_attr_init( &attr ); //初始化  
29     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); //是設置你想要指定線程屬性參數,這個參數代表這個線程是能夠join鏈接的,join功能表示主程序能夠等線程結束後再去作某事,實現了主程序和線程同步功能  
30     pthread_mutex_init( &sum_mutex, NULL ); //對鎖進行初始化      
31   
32     for( int i = 0; i < NUM_THREADS; ++i )  
33     {  
34         indexes[i] = i;  
35         int ret = pthread_create( &tids[i], &attr, say_hello, ( void* )&( indexes[i] ) ); //5個進程同時去修改sum  
36         if( ret != 0 )  
37         {  
38         cout << "pthread_create error:error_code=" << ret << endl;  
39     }  
40     }   
41     pthread_attr_destroy( &attr ); //釋放內存   
42     void *status;  
43     for( int i = 0; i < NUM_THREADS; ++i )  
44     {  
45     int ret = pthread_join( tids[i], &status ); //主程序join每一個線程後取得每一個線程的退出信息status  
46     if( ret != 0 )  
47     {  
48         cout << "pthread_join error:error_code=" << ret << endl;  
49     }  
50     }  
51     cout << "finally sum is " << sum << endl;  
52     pthread_mutex_destroy( &sum_mutex ); //註銷鎖  
53 }  

 

 

測試結果:

 1 wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_5  
 2 hello in thread hello in thread hello in thread 410  
 3 before sum is hello in thread 0 in thread 4  
 4 after sum is 4 in thread 4hello in thread   
 5   
 6   
 7 2  
 8 3  
 9 before sum is 4 in thread 1  
10 after sum is 5 in thread 1  
11 before sum is 5 in thread 0  
12 after sum is 5 in thread 0  
13 before sum is 5 in thread 2  
14 after sum is 7 in thread 2  
15 before sum is 7 in thread 3  
16 after sum is 10 in thread 3  
17 finally sum is 10  

 

可知,sum的訪問和修改順序是正常的,這就達到了多線程的目的了,可是線程的運行順序是混亂的,混亂就是正常?

 

6.信號量的實現
信號量是線程同步的另外一種實現機制,信號量的操做有signal和wait,本例子採用條件信號變量pthread_cond_t tasks_cond;
信號量的實現也要給予鎖機制。

 

 1 #include <iostream>  
 2 #include <pthread.h>  
 3 #include <stdio.h>  
 4   
 5 using namespace std;  
 6   
 7 #define BOUNDARY 5  
 8   
 9 int tasks = 10;  
10 pthread_mutex_t tasks_mutex; //互斥鎖  
11 pthread_cond_t tasks_cond; //條件信號變量,處理兩個線程間的條件關係,當task>5,hello2處理,反之hello1處理,直到task減爲0  
12   
13 void* say_hello2( void* args )  
14 {  
15     pthread_t pid = pthread_self(); //獲取當前線程id  
16     cout << "[" << pid << "] hello in thread " <<  *( ( int* )args ) << endl;  
17       
18     bool is_signaled = false; //sign  
19     while(1)  
20     {  
21     pthread_mutex_lock( &tasks_mutex ); //加鎖  
22     if( tasks > BOUNDARY )  
23     {  
24         cout << "[" << pid << "] take task: " << tasks << " in thread " << *( (int*)args ) << endl;  
25         --tasks; //modify  
26     }  
27     else if( !is_signaled )  
28     {  
29         cout << "[" << pid << "] pthread_cond_signal in thread " << *( ( int* )args ) << endl;  
30         pthread_cond_signal( &tasks_cond ); //signal:向hello1發送信號,代表已經>5  
31         is_signaled = true; //代表信號已發送,退出此線程  
32     }  
33     pthread_mutex_unlock( &tasks_mutex ); //解鎖  
34     if( tasks == 0 )  
35         break;  
36     }      
37 }  
38   
39 void* say_hello1( void* args )  
40 {  
41     pthread_t pid = pthread_self(); //獲取當前線程id  
42     cout << "[" << pid << "] hello in thread " <<  *( ( int* )args ) << endl;  
43   
44     while(1)  
45     {  
46         pthread_mutex_lock( &tasks_mutex ); //加鎖  
47         if( tasks > BOUNDARY )  
48         {  
49         cout << "[" << pid << "] pthread_cond_signal in thread " << *( ( int* )args ) << endl;  
50         pthread_cond_wait( &tasks_cond, &tasks_mutex ); //wait:等待信號量生效,接收到信號,向hello2發出信號,跳出wait,執行後續   
51         }  
52         else  
53         {  
54         cout << "[" << pid << "] take task: " << tasks << " in thread " << *( (int*)args ) << endl;  
55             --tasks;  
56     }  
57         pthread_mutex_unlock( &tasks_mutex ); //解鎖  
58         if( tasks == 0 )  
59             break;  
60     }   
61 }  
62   
63   
64 int main()  
65 {  
66     pthread_attr_t attr; //線程屬性結構體,建立線程時加入的參數  
67     pthread_attr_init( &attr ); //初始化  
68     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); //是設置你想要指定線程屬性參數,這個參數代表這個線程是能夠join鏈接的,join功能表示主程序能夠等線程結束後再去作某事,實現了主程序和線程同步功能  
69     pthread_cond_init( &tasks_cond, NULL ); //初始化條件信號量  
70     pthread_mutex_init( &tasks_mutex, NULL ); //初始化互斥量  
71     pthread_t tid1, tid2; //保存兩個線程id  
72     int index1 = 1;  
73     int ret = pthread_create( &tid1, &attr, say_hello1, ( void* )&index1 );  
74     if( ret != 0 )  
75     {  
76         cout << "pthread_create error:error_code=" << ret << endl;  
77     }  
78     int index2 = 2;  
79     ret = pthread_create( &tid2, &attr, say_hello2, ( void* )&index2 );  
80     if( ret != 0 )  
81     {  
82         cout << "pthread_create error:error_code=" << ret << endl;  
83     }  
84     pthread_join( tid1, NULL ); //鏈接兩個線程  
85     pthread_join( tid2, NULL );   
86   
87     pthread_attr_destroy( &attr ); //釋放內存   
88     pthread_mutex_destroy( &tasks_mutex ); //註銷鎖  
89     pthread_cond_destroy( &tasks_cond ); //正常退出  
90 }  

 

 

測試結果:
先在線程2中執行say_hello2,再跳轉到線程1中執行say_hello1,直到tasks減到0爲止。

 

 

 1 wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_6  
 2 [3069823856] hello in thread 2  
 3 [3078216560] hello in thread 1[3069823856] take task: 10 in thread 2  
 4   
 5 [3069823856] take task: 9 in thread 2  
 6 [3069823856] take task: 8 in thread 2  
 7 [3069823856] take task: 7 in thread 2  
 8 [3069823856] take task: 6 in thread 2  
 9 [3069823856] pthread_cond_signal in thread 2  
10 [3078216560] take task: 5 in thread 1  
11 [3078216560] take task: 4 in thread 1  
12 [3078216560] take task: 3 in thread 1  
13 [3078216560] take task: 2 in thread 1  
14 [3078216560] take task: 1 in thread 1  

 

 

到此,對多線程編程有了一個初步的瞭解,固然還有其餘實現線程同步的機制,有待進一步探索。

 

 

C++ 多線程編程總結

         在開發C++程序時,通常在吞吐量、併發、實時性上有較高的要求。設計C++程序時,總結起來能夠從以下幾點提升效率:

  • l  併發
  • l  異步
  • l  緩存

下面將我日常工做中遇到一些問題例舉一二,其設計思想無非以上三點。

1任務隊列

1.1    以生產者-消費者模型設計任務隊列

  生產者-消費者模型是人們很是熟悉的模型,好比在某個服務器程序中,當User數據被邏輯模塊修改後,就產生一個更新數據庫的任務(produce),投遞給IO模塊任務隊列,IO模塊從任務隊列中取出任務執行sql操做(consume)。

  設計通用的任務隊列,示例代碼以下:

  詳細實現可參見:

  http://ffown.googlecode.com/svn/trunk/fflib/include/detail/task_queue_impl.h

 1 void task_queue_t::produce(const task_t& task_) {      
 2         lock_guard_t lock(m_mutex);
 3         if (m_tasklist->empty()){//! 條件知足喚醒等待線程
 4             m_cond.signal();
 5         }
 6         m_tasklist->push_back(task_);
 7     }
 8 int   task_queue_t::comsume(task_t& task_){
 9         lock_guard_t lock(m_mutex);
10         while (m_tasklist->empty())//! 當沒有做業時,就等待直到條件知足被喚醒{
11             if (false == m_flag){
12                 return -1;
13             }
14             m_cond.wait();
15         }
16         task_ = m_tasklist->front();
17         m_tasklist->pop_front();
18         return 0;
19 }

 

1.2    任務隊列使用技巧

1.2.1 IO 與 邏輯分離

  好比網絡遊戲服務器程序中,網絡模塊收到消息包,投遞給邏輯層後當即返回,繼續接受下一個消息包。邏輯線程在一個沒有io操做的環境下運行,以保障實時性。示例:

1 void handle_xx_msg(long uid, const xx_msg_t& msg){
2     logic_task_queue->post(boost::bind(&servie_t::proces, uid, msg));
3 }

 

  注意,此模式下爲單任務隊列,每一個任務隊列單線程。

1.2.2  並行流水線

         上面的只是完成了io 和 cpu運算的並行,而cpu中邏輯操做是串行的。在某些場合,cpu邏輯運算部分也可實現並行,如遊戲中用戶A種菜和B種菜兩種操做是徹底能夠並行的,由於兩個操做沒有共享數據。最簡單的方式是A、B相關的操做被分配到不一樣的任務隊列中。示例以下:    

 
1 void handle_xx_msg(long uid, const xx_msg_t& msg) {
2   logic_task_queue_array[uid % sizeof(logic_task_queue_array)]->post(
3     boost::bind(&servie_t::proces, uid, msg));
4 }

 

注意,此模式下爲多任務隊列,每一個任務隊列單線程。

1.2.3 鏈接池與異步回調

  好比邏輯Service模塊須要數據庫模塊異步載入用戶數據,並作後續處理計算。而數據庫模塊擁有一個固定鏈接數的鏈接池,當執行SQL的任務到來時,選擇一個空閒的鏈接,執行SQL,並把SQL 經過回調函數傳遞給邏輯層。其步驟以下:

  • n  預先分配好線程池,每一個線程建立一個鏈接到數據庫的鏈接
  • n  爲數據庫模塊建立一個任務隊列,全部線程都是這個任務隊列的消費者
  • n  邏輯層想數據庫模塊投遞sql執行任務,同時傳遞一個回調函數來接受sql執行結果

  示例以下:

 
1 void db_t:load(long uid_, boost::function<void (user_data_t&) func_){
2     //! sql execute, construct user_data_t user
3     func_(user)
4 }
5 void process_user_data_loaded(user_data_t&){
6     //! todo something
7 }
8 db_task_queue->post(boost::bind(&db_t:load, uid, func));

 

         注意,此模式下爲單任務隊列,每一個任務隊列多線程。

2. 日誌

         本文主要講C++多線程編程,日誌系統不是爲了提升程序效率,可是在程序調試、運行期排錯上,日誌是無可替代的工具,相信開發後臺程序的朋友都會使用日誌。常見的日誌使用方式有以下幾種:

  • n  流式,如logstream << "start servie time[%d]" << time(0) << " app name[%s]" << app_string.c_str() << endl;
  • n  Printf 格式如:logtrace(LOG_MODULE, "start servie time[%d] app name[%s]", time(0), app_string.c_str());

  兩者各有優缺點,流式是線程安全的,printf格式格式化字符串會更直接,但缺點是線程不安全,若是把app_string.c_str() 換成app_string (std::string),編譯被經過,可是運行期會crash(若是運氣好每次都crash,運氣很差偶爾會crash)。我我的鐘愛printf風格,能夠作以下改進:

  • l  增長線程安全,利用C++模板的traits機制,能夠實現線程安全。示例:
 
1 template<typename ARG1>
2 void logtrace(const char* module, const char* fmt, ARG1 arg1){
3     boost::format s(fmt);
4     f % arg1;
5 }

 

  這樣,除了標準類型+std::string 傳入其餘類型將編譯不能經過。這裏只列舉了一個參數的例子,能夠重載該版本支持更多參數,若是你願意,能夠支持9個參數或更多。

  • l  爲日誌增長顏色,在printf中加入控制字符,能夠再屏幕終端上顯示顏色,Linux下示例:printf("\033[32;49;1m [DONE] \033[39;49;0m") 

  更多顏色方案參見:

     http://hi.baidu.com/jiemnij/blog/item/d95df8c28ac2815cb219a80e.html

  • l  每一個線程啓動時,都應該用日誌打印該線程負責什麼功能。這樣,程序跑起來的時候經過top –H – p pid 能夠得知那個功能使用cpu的多少。實際上,個人每行日誌都會打印線程id,此線程id非pthread_id,而實際上是線程對應的系統分配的進程id號。

3. 性能監控

         儘管已經有不少工具能夠分析c++程序運行性能,可是其大部分仍是運行在程序debug階段。咱們須要一種手段在debug和release階段都能監控程序,一方面得知程序瓶頸之所在,一方面儘早發現哪些組件在運行期出現了異常。

         一般都是使用gettimeofday 來計算某個函數開銷,能夠精確到微妙。能夠利用C++的肯定性析構,很是方便的實現獲取函數開銷的小工具,示例以下

複製代碼
 1 struct profiler{
 2     profiler(const char* func_name){
 3         gettimeofday(&tv, NULL);
 4         m_func_name=func_name;
 5     }
 6     ~profiler(){
 7         struct timeval tv2;
 8         gettimeofday(&tv2, NULL);
 9         long cost = (tv.tv_sec - tv.tv_sec) * 1000000 + (tv.tv_usec - tv.tv_usec);
10         //! post to some manager
11     }
12     struct timeval tv;
13     const char * m_func_name;
14 };
15 #define PROFILER() profiler ____profiler_instance##__LINE__(__FUNCTION__)

 

複製代碼

 

         Cost 應該被投遞到性能統計管理器中,該管理器定時講性能統計數據輸出到文件中。

4 Lambda 編程

使用foreach 代替迭代器

         不少編程語言已經內建了foreach,可是c++尚未。因此建議本身在須要遍歷容器的地方編寫foreach函數。習慣函數式編程的人應該會很是鍾情使用foreach,使用foreach的好處多多少少有些,如:

         http://www.cnblogs.com/chsword/archive/2007/09/28/910011.html
         但主要是編程哲學上層面的。

示例:

 
1 void user_mgr_t::foreach(boost::function<void (user_t&)> func_){
2     for (iterator it = m_users.begin(); it != m_users.end() ++it){
3         func_(it->second);
4     }
5 }

 

  好比要實現dump 接口,不須要重寫關於迭代器的代碼

1 void user_mgr_t:dump(){
2     struct lambda {
3         static void print(user_t& user){
4             //! print(tostring(user);
5         }
6     };
7     this->foreach(lambda::print);
8 }

 


  實際上,上面的代碼變通的生成了匿名函數,若是是c++ 11 標準的編譯器,本能夠寫的更簡潔一些:

  this->foreach([](user_t& user) {} );

  可是我大部分時間編寫的程序都要運行在centos 上,你知道嗎它的gcc版本是gcc 4.1.2, 因此大部分時間我都是用變通的方式使用lambda函數。

Lambda 函數結合任務隊列實現異步

  常見的使用任務隊列實現異步的代碼以下:

1 void service_t:async_update_user(long uid){
2     task_queue->post(boost::bind(&service_t:sync_update_user_impl, this, uid));
3 }
4 void service_t:sync_update_user_impl(long uid){
5     user_t& user = get_user(uid);
6     user.update()
7 }

 

  這樣作的缺點是,一個接口要響應的寫兩遍函數,若是一個函數的參數變了,那麼另外一個參數也要跟着改動。而且代碼也不是很美觀。使用lambda可讓異步看起來更直觀,彷彿就是在接口函數中馬上完成同樣。示例代碼:

 
1 void service_t:async_update_user(long uid){
2     struct lambda {
3         static void update_user_impl(service_t* servie, long uid){
4             user_t& user = servie->get_user(uid);
5             user.update();
6         }
7     };
8     task_queue->post(boost::bind(&lambda:update_user_impl, this, uid));
9 }

 

  這樣當要改動該接口時,直接在該接口內修改代碼,很是直觀。

5. 奇技淫巧

利用shared_ptr 實現map/reduce

         Map/reduce的語義是先將任務劃分爲多個任務,投遞到多個worker中併發執行,其產生的結果經reduce彙總後生成最終的結果。Shared_ptr的語義是什麼呢?當最後一個shared_ptr析構時,將會調用託管對象的析構函數。語義和map/reduce過程很是相近。咱們只需本身實現講請求劃分多個任務便可。示例過程以下:

  • l  定義請求託管對象,加入咱們須要在10個文件中搜索「oh nice」字符串出現的次數,定義託管結構體以下:
 
 1 struct reducer{
 2     void set_result(int index, long result) {
 3         m_result[index] = result;
 4     }
 5     ~reducer(){
 6         long total = 0;
 7         for (int i = 0; i < sizeof(m_result); ++i){
 8             total += m_result[i];
 9         }
10         //! post total to somewhere
11     }
12     long m_result[10];
13 };

 

  • l  定義執行任務的 worker
1 void worker_t:exe(int index_, shared_ptr<reducer> ret) {
2   ret->set_result(index, 100);
3 }

 


  • l  將任務分割後,投遞給不一樣的worker
  • 1 shared_ptr<reducer> ret(new reducer());
    2 for (int i = 0; i < 10; ++i)
    3 {
    4     task_queue[i]->post(boost::bind(&worker_t:exe, i, ret));
    5 }
相關文章
相關標籤/搜索