mutex和CRITICAL_SECTION,互斥和臨界區


本文不沒有任何知識可講,只是帖上自己測試的結果。

想看底層原理的可以直接關閉。

不過對於急着要選方案的人,倒提供一些幫助。

先說一些無關緊要的廢話:

====================================================================================================================================================

先說說為什么會有這篇文章。

我在做練習的時候,參考一些老代碼,發現了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 }
main.cpp

測試環境:win10企業版(已經更新到最新)+vs2015企業版+i7-6700HQ(2.6G×8)

64位release版結果:

圖中除最后一個外,都是循環1千萬次的結果。最后一個是10億次的結果。

再上一個64位debug版的1億次的截圖(原諒我沒有等帶10億次的結果,你們不知道,我測試1千萬的結果是n秒。然后頭繞一熱直接跳了兩級,一運行,發現沒出結果,然后一算,就傻了,關掉減個0)。

無論前面有多少經歷,無論多少推測。結果勝過一切,我可以繼續安心的、開心的使用std繼續進行我的練習了。

本次測試結果:

1、性能不是瓶頸,不要考慮太多。優化都是在原有的基礎上逐步修改改出來的成果,不是動手的時候,腦子就有現成的方案。何況性能並沒有走到瓶頸。

2、沒有什么比電腦跑出來的結果更靠譜。畢竟電腦才是所有理論知識最終產物的執行者。

3、隨時間的推移,技術在改良。使用通用的接口,每次技術更替,你也在享受免費的紅利。

最后,如果有朋友發現我的代碼中存在影響測試結果的錯誤,請留言指出。我不想自己錯了,還誤導別人。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM