Golang並發不安全,go高並發解決方案

本文目錄一覽:

為什麼用golang作為遊戲服務端的開發語言,它的並發性如何

你想想,如果你對怎麼用C語言寫高並發程序一無所知,你上golang就可以自動寫出高並發程序了?其實很多時候幻覺就發生在這裡。

我當初用delphi的時候還是個傻逼,現在用C++已經牛逼了,有時候我就總是會覺得C++寫出來的程序就是比delphi的bug少,雖然這顯然不成立。

golang語言map的並發和排序

golang語言map的並發和排序

golang預設的map不是thread safe的,如果存在讀寫並發的使用場景,必須在外面使用lock機制。

包sync裡面引入一個安全map;

用法:

運行結果如下:

golang官方說法map並不排序,不按key排序,也不按插入順序排序,也就是說map是無序的,無法保證任何排序。

下面是一種常見的給map排序輸出的辦法:

【golang】高並發下TCP常見問題解決方案

首先,看一下TCP握手簡單描繪過程:

其握手過程原理,就不必說了,有很多詳細文章進行敘述,本文只關注研究重點。

在第三次握手過程中,如果伺服器收到ACK,就會與客戶端建立連接,此時內核會把連接從半連接隊列移除,然後創建新的連接,並將其添加到全連接隊列,等待進程調用。

如果伺服器繁忙,來不及調用連接導致全連接隊列溢出,伺服器就會放棄當前握手連接,發送RST給客戶端,即connection reset by peer。

在linux平台上,客戶端在進行高並發TCP連接處理時,最高並發數量都要受系統對用戶單一進程同時打開文件數量的限制(這是因為系統每個TCP都是SOCKET句柄,每個soker句柄都是一個文件),當打開連接超過限制,就會出現too many open files。

使用下指令查看最大句柄數量:

增加句柄解決方案

如何模擬千萬並發 golang

學習了go的基本的並發變成模式,思路就是一個用通信來共享數據,而並不是像java一樣共享內存來通訊。go採用了用channel來傳遞消息,每一個協程持有一個信道,當信道可用時便可以讀寫數據,各信道間的處理數據互不影響。回想一下java中的並發編程,通常我們是因為操作一個數據而採用多線程並發訪問,比較明顯的是更新cache中的key對應的value.

讓我更是歡喜的時在golang中提供了sync.Once這個神器,從此做系統級的開關不再苦惱,天然的保證了就算多個協程並發的情況下也只有一個協程執行once.Do(func()),其他的協程阻塞。你再想想java裡面完成一個系統級初始化,做到並發安全且一次,你要搞一個boolean、再搞把鎖,再寫邏輯,神啊想想頭都大了。

Golang 並發讀寫map安全問題詳解

下面先寫一段測試程序,然後看下運行結果:

運行結果:

發生了錯誤,提示:fatal error: concurrent map read and map write, map 發生了同時讀和寫了; 但是這個錯誤並不是每次運行都會出現,就是有的時候會出現,有的時候並不會出現,根據筆者多次運行結果(其他例子,讀者可以自己嘗試下)來看還會有另外一種報錯就是:fatal error: concurrent map writes,就是map發生了同時寫,但是只是讀是不會有問題的。關於不同的運行結果小夥伴們可以自己寫幾個例子去測試下。下面就這兩個錯誤的發生,筆者給出如下解釋:

(1) fatal error: concurrent map read and map write

就是當一個goroutine在寫數據,而同時另外一個goroutine要讀數據就會報錯,不過這個報錯也很好理解:還沒寫完就讀,讀的數據會有問題,或者反過來還沒讀完就開始寫了,同樣會導致讀取的數據有問題;

(2) fatal error: concurrent map writes

兩個goroutine 同時寫一個內存地址,這種操作也是不允許的,會導致一些比較奇怪的問題;

總體來看其實就是寫map的操作和其他的讀或者寫同時發生了,導致的報錯,做過幾年開發的人可能會想到使用鎖來解決,比如寫map某個key的時候,通過鎖來保證其他goroutine不能再對其寫或者讀了。

實現思路:

(1) 當寫map的某個key時,通過鎖來保證其他goroutine不能再對其寫或者讀了。

(2) 當讀map的某個key時,通過鎖來保證其他的goroutine不能再對其寫,但是可以讀。

於是我們馬上想到golang 的讀寫鎖貌似符合需求,下面來實現下:

再來看下運行結果:

發現沒有報錯了,並且多次運行的結果都不會報錯,說明這個方法是有用的,不過在go1.9版本後就有sync.Map了,不過這個適用場景是讀多寫少的場景,如果寫很多的話效率比較差,具體的原因在這裡筆者就不介紹了,後面會寫篇文章詳細介紹下。

今天的文章就到這裡了,如果有不對的地方歡迎小夥伴給我留言,看到會即時回復的。

Go sync/atomic包Load和Store並發不安全

前言:為了保證並發安全,go語言中可以使用原子操作。其執行過程不能被中斷,這也就保證了同一時刻一個線程的執行不會被其他線程中斷,也保證了多線程下數據操作的一致性。

在atomic包中對幾種基礎類型提供了原子操作,包括int32,int64,uint32,uint64,uintptr,unsafe.Pointer。

對於每一種類型,提供了五類原子操作分別是

Load和Store操作對應與變數的原子性讀寫,許多變數的讀寫無法在一個時鐘周期內完成,而此時執行可能會被調度到其他線程,無法保證並發安全。

⚠️Load 只保證讀取的不是正在寫入的值,Store只保證寫入是原子操作。

所以在使用的時候要注意。

下面簡單示例:

點擊?查看源碼,哈哈哈…

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/280593.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-21 13:04
下一篇 2024-12-21 13:04

相關推薦

發表回復

登錄後才能評論