本文不沒有任何知識可講,只是帖上自己測試的結果。
想看底層原理的可以直接關閉。
不過對於急着要選方案的人,倒提供一些幫助。
先說一些無關緊要的廢話:
====================================================================================================================================================
先說說為什么會有這篇文章。
我在做練習的時候,參考一些老代碼,發現了CRITICAL_SECTION這個類型。以前沒有用過。查了一下,三個要點:windows使用;互斥效果;比mutex快。
后來又翻了些網頁查看兩者的一些簡介。很統一的結果,CRITICAL_SECTION比mutex快,而且Linux上沒有類似的接口(注:可能是我搜索的方式不對,加上本人對Linux研究不多,所以沒有找到)。
對於剛打算使用c++11制作全新技術接口版本的服務器的我來說,很遺憾啊,Linux沒有。難道我只能用這種慢速的鎖?而我又不想寫太多差異化的代碼,能用標准庫最好,等到真的某個模塊成為新能瓶頸的時候再針對某個模塊在Linux上做差異處理。
所以我還是想使用標准庫來完成這個事情。
於是,我搜索“CRITICAL_SECTION c++11”。
兩篇很重要的文章出現在了搜索結果里,是因為這兩篇文章而產生了本文。
https://stackoverflow.com/questions/23519630/are-there-c11-critical-sections
https://stackoverflow.com/questions/9997473/stdmutex-performance-compared-to-win32-critical-section
====================================================================================================================================================
第一篇文章的精要在:
雖然Linux上沒有臨界區這樣的接口,而mutex又是需要陷入內核去處理的東西。但是呢這些都是規定,僅僅是為了兼容POSIX協議做的。而mutex慢主要是POSIX需要跨進程。但是呢,在不同的系統和版本上面,就可以有私人定制,就如同windows上的CRITICAL_SECTION。一旦我不再兼容POSIX,就可以做一些自己的花活。而同時在兼容POSIX的平台上,繼續遵循POSIX的規定。
以上精要,你可以在第一個連接的第一個回復里面的追問里面得到。
這給我提供了一個很重要的信息:c++11是沒有臨界區這樣的用法。而且mutex的跨進程也不是所有的系統和版本都需要的,僅僅是某些版本需要。在不需要的版本上std::mutex可能是有特殊的用法和優化可以媲美臨界區。
總之,mutex和mutex不一樣
有了這個想法,我決定自己寫代碼試試。
然而不幸的是,當我准備寫的時候,我想,這種問題應該也會有其他人這樣想吧,說不定能搜到呢?
在搜索結果里,我就看到了第二篇。
第二篇文章的精要在:
std::mutex慢。CRITICAL_SECTION更快。但是如果采用合理的方式來分割任務,兩者可以達到幾乎相同的效果。
第二篇文章是含有兩個人的測試代碼的。第一個人的測試代碼是直接比對兩種用法的時間差異。但是很遺憾,他使用的是vs2012。這個版本對c++11的支持並不算完美。第二個人的測試代碼是將任務做了分割,分給不同的cpu,又延長了執行間隔,減少訪問沖突。使用的是vs2013,這一傳說中對c++11支持很完善的版本
看到這里,我有些冷,就不太想寫測試代碼了。原因是開發工具,人家已經更新到了一個合理的版本,其次在結構上進行了划分,而划分之后才打個平手。
似乎所有的結果都是唯一的。
但是!中間好幾年了。萬一有變化呢?即使沒有,自己測試一下總歸實在一些。所以還是自己做了個測試。結果很意外。
先說思路:
在同一進程中,開啟4個線程,2個用std::mutext去搶,兩個用CRITICAL_SECTION去搶;
兩組方式各自使用自己組的變量;
只記錄計算次數,不做結果正確判斷;
以下是測試代碼

1 #include <iostream> 2 #include <mutex> 3 #include <thread> 4 #include <Windows.h> 5 #include <chrono> 6 7 using namespace std; 8 9 mutex g_Mutex_Lock, g_Mutex_finish; 10 CRITICAL_SECTION g_CS_Lock, g_CS_finish; 11 uint64_t g_Mutext_Num = -1; 12 uint64_t g_CS_Num = -1; 13 const int32_t g_Count = 10000000; 14 once_flag g_Mutex_flag, g_CS_flag; 15 chrono::time_point<chrono::system_clock> g_Mutex_StartTime, g_CS_StartTime; 16 int32_t g_Mutex_Complete = 0; 17 int32_t g_CS_Complete = 0; 18 19 uint64_t Calculate(uint64_t num, int index) 20 { 21 if (index % 2) 22 { 23 return (num / 0x5555) * 0xaaaa; 24 } 25 else 26 { 27 return (num / 0x6666) * 0x9999; 28 } 29 } 30 31 void mutexTimeStart() 32 { 33 g_Mutex_StartTime = chrono::system_clock::now(); 34 } 35 36 void mutexCalculate() 37 { 38 call_once(g_Mutex_flag, mutexTimeStart); 39 40 for (int i = 0; i < g_Count; ++i) 41 { 42 g_Mutex_Lock.lock(); 43 g_Mutext_Num = Calculate(g_Mutext_Num, i); 44 g_Mutex_Lock.unlock(); 45 } 46 g_Mutex_finish.lock(); 47 ++g_Mutex_Complete; 48 if (2 == g_Mutex_Complete) 49 { 50 chrono::duration<double> elapsed_seconds = chrono::system_clock::now() - g_Mutex_StartTime; 51 printf("mutex finished use: %f\n", elapsed_seconds.count()); 52 } 53 g_Mutex_finish.unlock(); 54 } 55 56 void csTimeStart() 57 { 58 g_CS_StartTime = chrono::system_clock::now(); 59 } 60 61 void csCalculate() 62 { 63 call_once(g_CS_flag, csTimeStart); 64 for (int i = 0; i < g_Count; ++i) 65 { 66 EnterCriticalSection(&g_CS_Lock); 67 g_CS_Num = Calculate(g_CS_Num, i); 68 LeaveCriticalSection(&g_CS_Lock); 69 } 70 EnterCriticalSection(&g_CS_finish); 71 ++g_CS_Complete; 72 if (2 == g_CS_Complete) 73 { 74 chrono::duration<double> elapsed_seconds = chrono::system_clock::now() - g_CS_StartTime; 75 printf("cs finished use: %f\n", elapsed_seconds.count()); 76 } 77 LeaveCriticalSection(&g_CS_finish); 78 } 79 80 81 void main() 82 { 83 InitializeCriticalSection(&g_CS_Lock); 84 InitializeCriticalSection(&g_CS_finish); 85 86 thread t3(csCalculate); 87 t3.detach(); 88 thread t4(csCalculate); 89 t4.detach(); 90 91 thread t1(mutexCalculate); 92 t1.detach(); 93 thread t2(mutexCalculate); 94 t2.detach(); 95 96 int tStop; 97 cin >> tStop; 98 }
測試環境:win10企業版(已經更新到最新)+vs2015企業版+i7-6700HQ(2.6G×8)
64位release版結果:
圖中除最后一個外,都是循環1千萬次的結果。最后一個是10億次的結果。
再上一個64位debug版的1億次的截圖(原諒我沒有等帶10億次的結果,你們不知道,我測試1千萬的結果是n秒。然后頭繞一熱直接跳了兩級,一運行,發現沒出結果,然后一算,就傻了,關掉減個0)。
無論前面有多少經歷,無論多少推測。結果勝過一切,我可以繼續安心的、開心的使用std繼續進行我的練習了。
本次測試結果:
1、性能不是瓶頸,不要考慮太多。優化都是在原有的基礎上逐步修改改出來的成果,不是動手的時候,腦子就有現成的方案。何況性能並沒有走到瓶頸。
2、沒有什么比電腦跑出來的結果更靠譜。畢竟電腦才是所有理論知識最終產物的執行者。
3、隨時間的推移,技術在改良。使用通用的接口,每次技術更替,你也在享受免費的紅利。
最后,如果有朋友發現我的代碼中存在影響測試結果的錯誤,請留言指出。我不想自己錯了,還誤導別人。