震驚!ConcurrentHashMap裡面也有死迴圈,作者留下的“彩蛋”瞭解一下?
JDK BUG
這篇文章,聊一下我最近才知道的一個關於 JDK 8 的 BUG 吧。
首先說一下我是怎麼發現這個 BUG 的呢?
大家都知道我對 Dubbo 有一定的關注,前段時間 Dubbo 2.7.7 釋出後我看了它的更新點,就是下面這個網址: https://github.com/apache/dubbo/releases/tag/dubbo-2.7.7
其中有 Bugfixes 這一部分:
每一個我都去簡單的看了一下,其他的 Bugfixes 或多或少都和 Dubbo 框架有一定的關聯性。但是上面紅框框起來的部分完全就是 JDK 的 Bug 了。
所以可以單獨拎出來說。
這個 Bug 我也是看到了這個地方才知道的,但是研究的過程中我發現,這個怎麼說呢:我懷疑這根本就不是 Bug ,這就是 Doug Lea 老爺子在釣魚執法。
為什麼這樣的說呢,大家看完本文就知道了。
Bug 穩定復現
點選 Dubbo 裡面的連結,我們可以看到具體的描述就是一個連結:
開啟這個連結:
https://bugs.openjdk.java.net/browse/JDK-8062841
我們可以看到:這個 Bug 是位於大名鼎鼎的 concurrent 包裡面的 computeIfAbsent 方法。
這個 Bug 在 JDK 9 裡面被修復了,修復人是 Doug Lea。
而我們知道 ConcurrentHashMap 就是 Doug Lea 的大作,可以說是“誰汙染誰治理”。
要了解這個 Bug 是怎麼回事,就必須先了解下面這個方法是幹啥的:
java.util.concurrent.ConcurrentHashMap#computeIfAbsent
從這個方法的第二個入參 mappingFunction 我們可以知道這是 JDK 8 之後提供的方法了。
該方法的含義是:當前 Map 中 key 對應的值不存在時,會呼叫 mappingFunction 函式,並且將該函式的執行結果(不為 null)作為該 key 的 value 返回。
比如下面這樣的:
初始化一個 ConcurrentHashMap ,然後第一次去獲取 key 為 why 的 value,沒有獲取到,直接返回 null。
接著呼叫 computeIfAbsent 方法,獲取到 null 後呼叫 getValue 方法,將該方法的返回值和當前的 key 關聯起來。
所以,第二次獲取的時候拿到了 “why技術”。
其實上面的程式碼的 17 行的返回值就是 “why技術”,只是我為了程式碼演示,再去呼叫了一次 map.get() 方法。
知道這個方法幹什麼的,接下來就帶大家看看 Bug 是什麼。
我們直接用這個問題裡面給的測試用例,地址:
https://bugs.openjdk.java.net/secure/attachment/23985/Main.java
我只是在第 11 行和第 21 行加入了輸出語句:
正常的情況下,我們希望方法正常結束,然後 map 裡面是這樣的:{AaAa=42,BBBB=42}
但是你把這個程式碼拿到本地去跑(需要 JDK 8 環境),你會發現,這個方法永遠不會結束。因為它在進行死迴圈。
這就是 Bug。
提問的藝術
知道 Bug 了,按理來說就應該開始分析原始碼,瞭解為啥出現了會出現這個 Bug。
但是我想先插播一小節提問的藝術。因為這個 Bug 就是一個活生生的示例呀。
這個連結,我建議你開啟看看,這裡面還有 Doug Lea 老爺子的親自解答:
https://bugs.openjdk.java.net/browse/JDK-8062841
首先我們看提出問題的這個人對於問題的描述(可以先不用細看,反正看著也是懵逼的):
通常情況下,被提問的人分為兩類人:
1.遇到過並知道這個問題的人,可以看的明白你在說什麼。
2.雖然沒有碰見過這個問題,但感覺是自己熟悉的領域,可能知道答案,但是看了你的問題描述,也不知道你在說什麼。
這個描述很長,我第一次看的時候很懵逼,很難理解他在說什麼。我就是屬於第二類人。
而且在大多數的問題中,第二類人比第一類人多很多。
但是當我瞭解到這個 Bug 的來龍去脈的時候,再看這個描述,其實寫的很清楚了,也很好理解。我就變成第一類人了。
但是變成第一類人是有前提的,前提就是我已經瞭解到了這個地方 Bug 了。可惜,現在是提問,而被提問的人,還對這個 Bug 不是特別瞭解。
即使,這個被提問的人是 Doug Lea。
可以看到,2014 年 11 月 04 日 Martin 提出這個問題後, Doug Lea 在不到一個小時內就進行了回覆,我給大家翻譯一下,老爺子回覆的啥:
首先,你說你發現了 ConcurrentHashMap 的問題,但是我沒有看到的測試用例。那麼我就猜測一下是不是有其他執行緒在計算值的時候被卡住了,但是從你的描述中我也看不到相應的點。
簡單來說就是:Talk is cheap. Show me the code.(屁話少說,放碼過來。)
於是另一個哥們 Pardeep 在一個月後提交了一個測試案例,就是我們前面看到的測試案例:
Pardeep 給 Martin 回覆到下面這段話:
他開門見山的說:我注意這個 bug 很長時間了,然後我還有一個測試用例。
可以說這個測試案例的出現,才是真正的轉折點。
然後他提出了自己的看法,這段描述簡短有力的說出了問題的所在(後面我們會講到),然後他還提出了自己的意見。
不到一個小時,這個回到得到了 Doug Lea 的回覆:
他說:小夥子的建議還是不錯的,但是現在還不是我們解決這個問題的時候。我們也許會通過程式碼改進死鎖檢查機制,以幫助使用者 debug 他們的程式。但是目前而言,這種機制就算做出來,工作效率也是非常低下的,比如在當前的這個案例下。但是現在我們至少清楚的知道,是否要實現這種機制是不能確定的。
總之一句話:問題我知道了,但是目前我還沒想到好的解決方法。
但是,在 19 天以後,老爺子又回來處理這個問題了:
這次的回答可謂是峰迴路轉,他說:請忽略我之前的話。我們發現了一些可行的改進方法,這些改進可以處理更多的使用者錯誤,包括本報告中所提供的測試用例,即解決在 computeIfAbsent 中提供的函式中進行遞迴對映更新導致死鎖這樣的問題。我們會在 JDK 9 裡面解決這個問題。
所以,回顧這個 Bug 被提出的過程。
首先是 Martin 提出了這個問題,並進行了詳細的描述。可惜的是他的描述很專業,是站在你已經瞭解了這個 Bug 的立場上去描述的,讓人看的很懵逼。
所以 Doug Lea 看到後也表示這啥呀,沒搞懂。
然後是 Pardeep 跟進這個問題,轉折點在於他丟擲的這個測試案例。而我相信,既然 Martin 能把這個問題描述的很清楚,他一定是有一個自己的測試案例的,但是他沒有展現出來。
所以,朋友們,測試案例的重要性不言而喻了。問問題的時候不要只是丟擲異常,你至少給段對應的程式碼,或者日誌,或者一次性描述清楚,寫在文件裡面發出來也行呀。
Bug 的原因
導致這個 Bug 的原因也是一句話就能說清楚,前面的 Pardeep 老哥也說了:
問題在於我們在進行 computeIfAbsent 的時候,裡面還有一個 computeIfAbsent。而這兩個 computeIfAbsent 它們的 key 對應的 hashCode 是一樣的。
你說巧不巧。
當它們的 hashCode 是一樣的時候,說明它們要往同一個槽放東西。
而當第二個元素進來的時候,發現坑位已經被前一個元素佔領了,可能就是這樣的畫風:
接下來我們就解析一下 computeIfAbsent 方法的工作流程:
第一步是計算 key 對應的 hashCode 應該放到哪個槽裡面。
然後是進入1649 行的這個 for 迴圈,而這個 for 迴圈是一個死迴圈,它在迴圈體內部判斷各種情況,如果滿足條件則 break 迴圈。
首先,我們看一下 “AaAa” 和 “BBBB” 經過 spread 計算(右移 16 位高效計算)後的 h 值是什麼:
哇塞,好巧啊,從框起來的這兩部分可以看到,都是 2031775 呢。
說明他們要在同一個槽裡面搞事情。
先是 “AaAa” 進入 computeIfAbsent 方法:
在第一次迴圈的時候 initTable,沒啥說的。
第二次迴圈先是在 1653 行計算出陣列的下標,並取出該下標的 node。發現這個 node 是空的。於是進入分支判斷:
在標號為 ① 的地方進行 cas 操作,先用 r(即 ReservationNode)進行一個佔位的操作。
在標號為 ② 的地方進行 mappingFunction.apply 的操作,計算 value 值。如果計算出來不為 null,則把 value 組裝成最終的 node。
在標號為 ③ 的東西把之前佔位的 ReservationNode 替換成標號為 ② 的地方組裝成的node 。
問題就出現標號為 ② 的地方。可以看到這裡去進行了 mappingFunction.apply 的操作,而這個操作在我們的案例下,會觸發另一次 computeIfAbsent 操作。
現在 “AaAa” 就等著這個 computeIfAbsent 操作的返回值,然後進行下一步操作,也就是進行標號為 ③ 的操作了。
接著 “BBBB” 就來了。
通過前面我們知道了 “BBBB” 的 hashCode 經過計算後也是和 “AaAa” 一樣。所以它也要想要去那個槽裡面搞事情。
可惜它來晚了一步。
帶大家看一下對應的程式碼:
當 key 為 “BBBB” 的時候,算出來的 h 值也是 2031775。
它也會進入 1649 行的這個死迴圈。然後進行各種判斷。
接下來我要論證的是:
在本文的示例程式碼中,當執行到 key 為 “BBBB” 的時候,進入 1649 行這個死迴圈後,就退不出來了。程式一直在裡面迴圈執行。
在標號為 ① 的地方,由於這個時候 tab 已經不為 null 了,所以不會進入這個分支。
在標號為 ② 的地方,由於之前 “AaAa” 已經扔了一個 ReservationNode 進去佔位置了,所以不等於 null。所以,也就不會進入這個分支。
怕你懵逼,給你配個圖,真是暖男作者石錘了:
接下來到標號為 ③ 的地方,裡面有一個 MOVED,這個 MOVED 是幹啥的呢?
表示當前的 ConcurrentHashMap 是否是在進行擴容。
很明顯,現在還沒有到該擴容的時候:
第 1678 行的 f 就是之前 “AaAa” 扔進去的 ReservationNode ,這個 Node 的 hash 是 -3,不等於MOVED(-1)。
所以,不會進入這個分支判斷。
接下來,能進的只有標號為 ④ 的地方了,所以我們只需要把這個地方攻破,就徹底瞭解這個 Bug 了。
走起:
通過前面的分析我們知道了,當前案例情況下,只會進入 1672 行這個分支。
而這個分支裡面,還有四個判斷。我們一個個的攻破:
標號為 ⑤ 的地方,tabAt 方法取出來的物件,就是之前 “AaAa” 放進去的佔位的 ReservationNode ,也就是這個 f 。所以可以進入這個分支判斷。
標號為 ⑥ 的地方,fh >=0 。而 fh 是當前 node 的 hash 值,大於 0 說明當前是按照連結串列儲存的資料。之前我們分析過了,當前的 hash 值是 -3。所以,不會進入這個分支。
標號為 ⑦ 的地方,判斷 f 節點是否是紅黑樹儲存。當然不是的。所以,不會進入這個分支。
標號為 ⑧ 的地方,binCount 代表的是該下標裡面,有幾個 node 節點。很明顯,現在一個都沒有。所以當前的 binCount 還是 0 。所以,不會進入這個分支。
完了。分析完了。
Bug 也就出來了,一次 for 迴圈結束後,沒有 break。苦就苦在這個 for 迴圈還是個死迴圈。
再來一個上帝視角,看看當 key 為 “BBBB” 的時候發生了什麼事情:
進入無限迴圈內:
①.經過 “AaAa” 之後,tab 就不為 null 了。
②.當前的槽中已經被 “AaAa” 先放了一個 ReservationNode 進行佔位了,所以不為 null。
③.當前的 map 並沒有進行擴容操作。
④.包含⑤、⑥、⑦、⑧。
⑤.tabAt 方法取出來的物件,就是之前 “AaAa” 放進去的佔位的 ReservationNode,所以滿足條件進入分支。
⑥.判斷當前是否是連結串列儲存,不滿足條件,跳過。
⑦.判斷當前是否是紅黑樹儲存,不滿足條件,跳過。
⑧.判斷當前下標裡面是否放了 node,不滿足條件(“AaAa” 只有個佔位的Node ,並沒有初始完成,所以還沒有放到該下標裡面),進入下一次迴圈。
然後它就在死迴圈裡面出不來了!
我相信現在大家對於這個 Bug 的來路瞭解清楚了。
如果你是在 idea 裡面跑這個測試用例,也可以這樣直觀的看一眼:
點選這個照相機圖示:
從執行緒快照裡面其實也是可以看到端倪的,大家可以去分析分析。
有的觀點說的是由於執行緒安全的導致的死迴圈,經過分析我覺得這個觀點是不對的。
它存在死迴圈,不是由於執行緒安全導致的,純粹是自己進入了死迴圈。
或者說,這是一個“彩蛋”?
或者......自信點,就說這事 Bug ,能穩定復現的那種。
那麼我們如果是使用 JDK 8 怎麼避免踩到這個“彩蛋”呢?
看看 Dubbo 裡面是怎麼解決的:
先呼叫了 get 方法,如果返回為 null,則呼叫 putIfAbsent 方法,這樣就能實現和之前一樣的效果了。
如果你在專案中也有使用 computeIfAbsent 的地方,建議也這樣去修改。
說到 ConcurrentHashMap get 方法返回 null,我就想起了之前討論的一個面試題了:
答案都寫在這個文章裡面了,有興趣的可以瞭解一下《這道面試題我真不知道面試官想要的回答是什麼》
Bug 的解決 其實徹底理解了這個 Bug 之後,我們再來看一下 JDK 9 裡面的解決方案,看一下官方原始碼對比:
http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/ConcurrentHashMap.java?r1=1.258&r2=1.259&sortby=date&diff_format=f
就加了兩行程式碼,判斷完是否是紅黑樹節點後,再判斷一下是否是 ReservationNode 節點,因為這個節點就是個佔位節點。如果是,則丟擲異常。
就這麼簡單。沒有什麼神祕的。
所以,如果你在 JDK 9 裡面執行文字的測試用例,就會丟擲 IllegalStateException。
這就是 Doug Lea 之前提到的解決方案:
瞭解了這個 Bug 的來龍去脈後,特別是看到解決方案後,我們就能輕描淡寫的說一句:
害,就這?沒聽說過!
另外,我看 JDK 9 修復的時候還不止修復了一個問題:
http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/6dd59c01f011/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java
你去翻一翻。發現,啊,全是知識點啊,學不動了。
釣魚執法
為什麼我在文章的一開始就說了這是 Doug Lea 在釣魚執法呢?
因為在最開始提問的藝術那一部分,我相信,Doug Lea 跑完那個測試案例之後,心裡也有點數了。
大概知道問題在哪了,而且從他的回答和他寫的文件中我也有理由相信,他寫的這個方法的時候就知道可能會出問題。
而且,Pardeep 的回覆中提到了文件,那我們就去看看官方文件對於該方法的描述是怎樣的:
https://docs.oracle.com/javase/8/docs/api/
文件中說函式方法應該簡短,簡單。而且不能在更新的對映的時候更新對映。就是說不能套娃。
套娃,用程式說就是recursive(遞迴),按照文件說如果存在遞迴,則會丟擲 IllegalStateException 。
而提到遞迴,你想到了什麼?
我首先就想到了斐波拉契函式。我們用 computeIfAbsent 實現一個斐波拉契函式如下:
public class Test {
static Map<Integer, Integer> cache = new ConcurrentHashMap<>();
public static void main(String[] args) {
System.out.println("f(" + 14 + ") =" + fibonacci(14));
}
static int fibonacci(int i) {
if (i == 0)
return i;
if (i == 1)
return 1;
return cache.computeIfAbsent(i, (key) -> {
System.out.println("Slow calculation of " + key);
return fibonacci(i - 2) + fibonacci(i - 1);
});
}
}
這就是遞迴呼叫,我用 JDK 1.8 跑的時候並沒有丟擲 IllegalStateException,只是程式假死了,原因和我們前面分析的是一樣一樣的。我理解這個地方是和文件不符的。
所以,我懷疑是 Doug Lea 在這個地方釣魚執法。
CHM一定執行緒安全嗎?
既然都說到 currentHashMap(CHM)了,那我說一個相關的注意點吧。
首先 CHM 一定能保證執行緒安全嗎?
是的,CHM 本身一定是執行緒安全的。但是,如果你使用不當還是有可能會出現執行緒不安全的情況。
給大家看一點 Spring 中的原始碼吧:
org.springframework.core.SimpleAliasRegistry
在這個類中,aliasMap 是 ConcurrentHashMap 型別的:
在 registerAlias 和 getAliases 方法中,都有對 aliasMap 進行操作的程式碼,但是在操作之前都是用 synchronized 把 aliasMap 鎖住了。
為什麼?為什麼我們操作 ConcurrentHashMap 的時候還要加鎖呢?
這個是根據場景而定的,這個別名管理器,在這裡加鎖應該是為了避免多個執行緒操作 ConcurrentHashMap 。
雖然 ConcurrentHashMap 是執行緒安全的,但是假設如果一個執行緒 put,一個執行緒 get,在這個程式碼的場景裡面是不允許的。
如果覺得不太好理解的話我舉一個 redis 的例子。
redis 的 get、set 方法都是執行緒安全的吧。但是你如果先 get 再 set,那麼在多執行緒的情況下還是會有問題的。
因為這兩個操作不是原子性的。所以 incr 就應運而生了。
我舉這個例子的是想說執行緒安全與否不是絕對的,要看場景。給你一個執行緒安全的容器,你使用不當還是會有執行緒安全的問題。
再比如,HashMap 一定是執行緒不安全的嗎?
說不能說的這麼死吧。它是一個執行緒不安全的容器。但是如果我的使用場景是隻讀呢?
在這個只讀的場景下,它就是執行緒安全的。
總之,看場景。道理,就是這麼一個道理。
最後說兩句(求關注)
所以點個“贊”吧,周更很累的,不要白嫖我,需要一點正反饋。
才疏學淺,難免會有紕漏,如果你發現了錯誤的地方,還請你留言指出來,我對其加以修改。
感謝您的閱讀,我堅持原創,十分歡迎並感謝您的關注。
我是 why,一個被程式碼耽誤的文學創作者,不是大佬,但是喜歡分享,是一個又暖又有料的四川好男人。
歡迎關注我的微信公眾號:why技術。在這裡我會分享一些java技術相關的知識,用匠心敲程式碼,對每一行程式碼負責。偶爾也會荒腔走板的聊一聊生活,寫一寫書評、影評。感謝你的關注,願你我共同進步。
相關推薦
震驚!ConcurrentHashMap裡面也有死迴圈,作者留下的“彩蛋”瞭解一下?
JDK BUG 這篇文章,聊一下我最近才知道的一個關於 JDK 8 的 BUG 吧。 首先說一下我是怎麼發現這個 BUG 的呢? 大家都知道我對 Dubbo 有一定的關注,前段時間 Dubbo 2.7.7 釋出後我看了它的更新點,就是下面這個網址: https://github.com/apache/dubb
ConcurrentHashMap竟然也有死迴圈問題?
前幾天和朋友閒聊,說遇到了一個ConcurrentHashMap死迴圈問題,當時心裡想這不科學呀?ConcurrentHashMap怎麼還有死迴圈呢,畢竟它已經解決HashMap中rehash中死迴圈問題了,但是隨著深入的分析,發現事情並沒有之前想的那麼簡單~ (以下分析基於jdk版本:jdk1
Android jar包裡面有assets目錄下有檔案,APP的assets裡面也有檔案,如何讀呢?
jar 包中可以寫 assets資料夾, APP裡面同時也可以寫assets,但是隻要assets下面的檔案不同名就可以了,忒棒 !!! 如果assets目錄下有檔案的,
Java併發容器ConcurrentHashMap原理及HashMap死迴圈原因的分析
HashMap是我們最常用的資料結構之一,它方便高效,但遺憾的是,HashMap是執行緒不安全的,在併發環境下,在HashMap的擴容過程中,可能造成散列表的迴圈鎖死。而執行緒安全的HashTable使用了大量Synchronized鎖,導致了效率非常低下。幸運的是,併發程
【Linux】使用者不在sudoers檔案中(不會vi的裡面也有使用到的步驟說明)
轉載自:http://blog.csdn.net/dreamback1987/article/details/8766302 *** is not in the sudoers file. This incident will be reported.” (使用者不
我靠!Semaphore裡面居然有這麼一個大坑!
這是why的第 59 篇原創文章 荒腔走板 大家好,我是why哥 ,歡迎來到我連續周更優質原創文章的第 59 篇。 上週寫了一篇文章,一不小心戳到了大家的爽點,其中一個轉載我文章的大號,閱讀量居然突破了 10w+,我也是受寵若驚。 但是其實我是一個技術博主來的,偶爾寫點生活相關的。所以這篇還是回到技術上。
乾頤堂既有老臘肉也有小鮮肉,歡迎大家來學習,大學生如何學習HCIE,答案來咯
乾頤堂安德hcie前邊給大家分享了工作20余年的劉大哥的經歷,下面給大家帶來20出頭沒畢業小夥子的在乾頤堂的學習經歷9月4號開學第一天收到了一份特別的禮物,PASS HCIE在HCIE的路上要一步一個腳印走過來,我從去年暑假之前加入乾頤堂這個大家庭,與很多在路上的HCIE一樣,從對TCP/IP的一無所知,到現
有1千萬條有重復的短信,以文本文件的形式保存,一行一條,也有重復,用5分鐘時間找出重復出現最多的前10條短信。
ascii碼 宋體 哈希表 文本 深度 pan 進行 span 二次 1,哈希表法 分成若幹組,進行掃描。相同記錄只加1次,重復次數加1,之後進行第二次掃描,得出重復次數的前十名。 2,從小到大排序(我覺得最好不用這種方法,準確性不高) 3,內存映射(建議) 對每條
一個有意思的小故事,希望以後也有這種修養,也能這樣厚道
而後 離開 十大 正在 一個 說道 給他 為什麽 我們 清朝的時候,常州有一個魏廉訪,他的父親是個醫生,樂善好施。 有一次魏老先生被請往一病人家中治病。 他離開後,病人發現藏在枕頭下的十兩銀子不見了。 病人的的兒子,懷疑是魏老先生拿了,就到老先生家,對他說:&ldqu
遊戲音樂製作也有經典法則,你瞭解多少
遊戲依靠畫面和劇情吸引玩家,背景音樂感染玩家,給玩家最真實的遊戲體驗,為了能使遊戲背景音樂有更好的表現,在實際的遊戲音樂製作過程中,也有自己的經典法則,今天跟著奇億音樂小編一起來看看這些法則是什麼。 業內人士都知道,音訊源
窮廟裡也有富和尚,看程式設計師工作單位和職業道路的選擇
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
經典案例那個什麼多執行緒--賣票..........................就是好像有點不對~~!先寫下來.有大哥哥,大姐姐幫看下嗎
package javawork; public class RunnableDemo2 { public static void main(String[] args) { Runnable02 ru = new Runnable02(); Thread ru1 = new Thread(ru); T
經典案例那個什麽多線程--賣票..........................就是好像有點不對~~!先寫下來.有大哥哥,大姐姐幫看下嗎
tst dem vat ole 多線程 快的 args str ring package javawork; public class RunnableDemo2 { public static void main(String[] args) { Runnable02
行情不好總會有漲停股票,也有賺錢效應,這話如何理解?
各位投資朋友大家好,歡迎收聽《搶財貓股票課堂》,我是你們的老朋友波哥。 我們經常有人說“行情不好總會有漲停股票,也有賺錢效應”,這話如何理解? 我覺得說這句話的人無非就想說,你賺不到錢不要怨這個市場,因為即便熊市也有漲停的股票,那你還是沒本事買到漲停的股票呀,所以你
[CSS] hover 死迴圈,不斷閃現。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style> .content{
Boost.Asio基本原理(CSDN也有Markdown了,好開森)
Boost.Asio基本原理 這一章涵蓋了使用Boost.Asio時必須知道的一些事情。我們也將深入研究比同步程式設計更復雜、更有樂趣的非同步程式設計。 網路API 這一部分包含了當使用Boost.Asio編寫網路應用程式時必須知道的事情。 Boost.
python寫快排 寫了死迴圈, 記下來警醒一下
寫 尋找最小k 個數 時,直接用快排寫。但是快排一開始寫錯了。。。 class Solution: def GetLeastNumbers_Solution(self, tinput, k): if tinput ==[] or k > len(
Boost.Asio入門(CSDN也有Markdown了,好開森)
Boost.Asio入門 首先,讓我們先來了解一下什麼是 Boost.Asio?怎麼編譯它?瞭解的過程中我們會給出一些例子。然後在發現 Boost.Asio 不僅僅是一個網路庫的同時你也會接觸到 Boost.Asio 中最核心的類——io_service。
前言(CSDN也有Markdown了,好開森)
實戰出精華 在具體的C++網路程式設計中提升你的逼格 John Torjo Boost.Asio C++ 網路程式設計 Copyright © 2013 Packt Publishing 關於作者 做為一名權威的C++專家,John Torj
在你沒有成功之前沒有人在乎你的感受! 這個世界上也有許多人,他們在乎你的感受,這並非與你是否成功有關係
這世界並不會在意你的自尊。 這世界指望你在自我感覺良好之前先要有所成就。 人生是不公平的,習慣去接受它吧 。 如果你覺得你的老闆很凶,等你當了老闆就知道了,老闆是沒有工作任期保障的。 站在鎂光燈下是墮落的,站在講臺上是墮落的;因為他們會讓你覺得,你比別人更聰明。