go語言的一些常見踩坑問題

開始之前,介紹一下?最近很火的開源技術,低代碼。

作為一種軟件開發技術逐漸進入了人們的視角里,它利用自身獨特的優勢占領市場一角——讓使用者可以通過可視化的方式,以更少的編碼,更快速地構建和交付應用軟件,極大程度地降低了軟件的開發、配置、部署和培訓成本。

應用地址: https://www.jnpfsoft.com
開發語言:Java/.net

這是一個基于 Java Boot/.Net Core 構建的簡單、跨平臺快速開發框架。前后端封裝了上千個常用類,方便擴展;采用微服務、前后端分離架構,集成了代碼生成器,支持前后端業務代碼生成,滿足快速開發;框架集成了表單、報表、圖表、大屏等各種常用的 Demo 方便直接使用;后端框架支持 Vue2、Vue3,平臺即可私有化部署,也支持 K8S 部署。

從其他語言剛轉入go語言的時候比較容易出現以下方面的問題:

  • 字符串string
  • interface斷言
  • 切片slices
  • map
  • 控制結構(for、switch)
  • defer channel管道
  • sync同步機制
  • select+timer
1.字符串String(Split分割)

項目可能使用情況:?當使用string的split功能分割空字符串時,再進行數據庫模糊查詢時候,如下:

img

踩坑分析:?當對空字符串進行Split,將會返回一個包含一個空字符串的切片數組,數組長度為1,但是查詢時由于空字符串會被過濾掉了該條件,會導致查詢出來的數據不正確,甚至可能會是全表掃,由于查詢所有數據可能會系統崩潰掉。

如何避坑:?使用前可以排除空字符串

img

2.interface斷言

在項目中也會經常使用類型斷言,當使用interface()轉化成相對應的類型時,如果不恰當使用斷言而導致panic,踩坑代碼:

img

踩坑分析:?golang中對于類型的斷言,一定需要加上第二個參數ok判斷,否則類型不一致的話直接panic退出?如何避坑:?增加第二個參數ok來判斷

img

3.切片slice
3.1 容量問題

要注意在make切片的時候的參數設置,參數設置有問題很容易導致取下標值不是自己想象中的值,如下:

img

踩坑分析:?一般來說,slice的初始化為 make([]T, length, capacity)。 如果省略了capacity,默認capacity等于length。因此上面建了一個[]int類型的切片,長度和容量為3的[0,0,0]切片,因此通過append(s,1)會使slice擴容成6,并添加元素1進去。輸出結果為:[0,0,0,1]

如何避坑:1.使用make([]T, length, capacity)補全參數;2.使用make([]T, length),則使用通過索引方式賦值,例如,s[0]=1

3.2?截取[:n]

在項目中可能會使用到切片截取功能,如下簡單的代碼,那么會出現什么問題呢?

img

踩坑分析:?因為切片的截取是引用關系,共有 2 個切片 a 和 b,截取了 a 的一部分賦值給了 b,兩者存在著關聯。圖3-2-1 因此,雖然切片 a 只有底層數組中 0 和 1 兩個索引位正在被使用,其余未使用的底層數組空間毫無作用,圖3-2-2。但由于正在被引用,他們也不會被 GC,因此造成了內存泄露。

img

圖3-2-1

img

圖3-2-2

如何避坑: 可以通過拷貝的方式,同時將原有的切片或者數組釋放。

img

4.map
4.1nil的map賦值問題

在項目中也經常使用到map,但是對于map的使用也很容易出錯,比如,對一個nil的map進行賦值:

img

踩坑分析:?對未初始化的map變量,添加元素時會空指針panic,拋出錯誤:

img

如何避坑:往map添加元素時需要先分配內存。 例如 m := make(map[int]int)

4.2?判斷map中的key是否存在

在使用map的key取值時,需要先判斷key是否存在,踩坑代碼:

img

如何避坑: 不能通過取出來的值來判斷key是否存在map中。需要采用如下的形式:

  if _, ok := m[1]; !ok {print("key not exists")}
?
4.3map的遍歷順序問題

在使用map for循環時,也會出現一些踩坑問題,比如,判斷map兩次循環相同順序的值是否一致。

img

踩坑分析:?map的遍歷時,golang會提前取一個隨機數,把桶的遍歷順序隨機化。因此,在程序中,不能依賴遍歷的順序。?如何避坑: 如果需要確保遍歷順序,一般需要自行維護一個額外的有序的數據結構。比如,使用list+sorts

img

4.4?map的并發讀寫

在使用map時需要注意,map寫入和讀取操作是否存在并發問題,特別是引用第三方庫的時候,比較容易出現并發map操作的問題,比如:

img

踩坑分析:?golang中的普通map不是線程安全的,如果并發讀寫,會導致panic。出現這樣的錯誤:

img

如何避坑:不要并發讀寫map,也即不要在多個goroutine中同時對map進行讀和寫。如果一定要有讀和寫,可以使用sync.Map,但是sync.Map性能比較低,小心使用。

5.控制結構
5.1for循環取址問題

在項目中經常使用for循環進行遍歷,但是很容易在指針類型上使用錯誤,比如:

img

踩坑分析:?因為在循環里創建的所有函數變量共享相同的變量,其實就是一個可訪問的存儲位置,而不是固定的值。 因此在for多次循環中,value的地址只有一個。比如,在上面的循環變量p中,在每次迭代中只給它分配了一個新值,而循環變量的地址在每次迭代中都是相同的,因此將存儲相同的指針。因此,上面的遍歷中,在循環之后,它將保存在最后一次迭代中分配的值。因此運行以上代碼,輸出如下,和預期不一樣:

img

如何避坑

(1).在上面的case中不要使用指針

(2).在本地賦予一個臨時指針,使用臨時指針進行賦值,就不會被覆蓋。

 for _, p := range persons {innerP := ppersonMap[p.name] = &innerP}
5.2?for必包問題

在項目中也經常使用for循環進行啟動協程,在使用協程的時候,需要注意的for循環體中的變量也是一樣,比如:

img

踩坑分析:?這個問題和上面的指針問題類似,因為for遍歷非常快,所以當for遍歷完畢后,v的值是最后的值。因此,在go閉包函數運行的時候,打印的全部都是最新的值。?如何避坑

在循環中的閉包,應該使用傳參的方式,將變量傳入函數中。這個時候會發生一次拷貝,因此,不會被其它的變量所覆蓋:

for _, v := range s {go func(v string) {println(v)}(v)
}
?

或者使用臨時變量,將循環體中值重新賦值給臨時變量中:

for _, v := range s {tempV:=vgo func() {println(tempV)}()
} 
5.3?switch多個case問題

在項目中也會使用到switch,但是由于go語言跟其他的語言的switch,也很容易誤以為多個case放在一起能夠接著執行,如下:

package mainimport "fmt"func main() {i := 1switch i {case 1:case 2:fmt.Println("ok")}fmt.Println("end")
}
?

踩坑分析:?golang的switch和其它語言差別很大。像Java/c等,上面的情況可能使case 1和case 2都執行到了下面的語句。但是golang會自動為每個case增加break。 因此,上面執行到了case 1之后就退出了。?如何避坑:如果需要上面的case滿足預期,可以在case1后面增加fallthrougth語句。 或者直接case1, 2多個條件一起。

package mainimport "fmt"func main() {i := 1switch i {case 1:fallthrougthcase 2:fmt.Println("ok")fallthrougth}fmt.Println("end")
}
?
6.defer問題
6.1?defer在跨協程的問題

在項目中defer經常在使用func方法最前面,進行捕獲一些非法異常,但是也很容易忽略了跨協程的問題,比如:

//PublishBusiness 發布 
func PublishBusiness(ctx context.Context, businessId int64) error {var e errordefer func() {if e !=nil{logger.CtxLogErrorf(ctx, "PublishBusiness err: %v", err)
?}}()//更新e = b.doPublishBusiness(c, businessId)go func() {1/0 //子協程 pianc}() return err
}
?

踩坑分析:?defer 只會在當前函數返回前執行傳入的函數,理解這句話主要在三個方面:當前函數返回前執行傳入的函數,即 defer 關鍵值后面跟的是一個函數,包括普通函數如(fmt.Println), 也可以是匿名函數 func() 因此,在使用recover時,必須在同一個goroutine中使用才可以捕獲panic。上面出現panic是在子goroutine中,因此無法捕獲,會導致程序crash中斷退出。?如何避坑:一般啟動一個goroutine時,必須在該goroutine中處理panic,使用defer捕獲一下。

6.2?循環中使用defer

在項目中會使用到for循環打開文件,但是在關閉文件的時候容易出現問題,比如:

package mainimport ("log""os"
)func main() {for i := 0; i < 10; i++ {f, err := os.Open("/path/file")if err != nil {log.Fatalln(err)}defer f.Close()}
}
?

踩坑分析:?因為defer是在整個函數運行完畢之后才會執行。因此上面的代碼中,會出現內存泄漏問題,因為在循環中,每個defer函數會壓入到堆棧中。等到整個main函數執行完畢,才從堆棧中彈出來defer函數進行執行。假如循環比較大,而且里面的執行比較重,那么會嚴重影響性能。

如何避坑:不要再for循環中使用defer函數。可以通過匿名函數將函數快速結束,從而快速執行defer函數釋放資源。例如:

package main
import ("log""os"
)
func main() {for i := 0; i < 10; i++ {func() {f, err := os.Open("/path/file")if err != nil {log.Println(err)return}defer f.Close()}()}
}
?
7.channel管道問題
7.1?channel管道panic的問題

項目經常使用協程并發,結果收集會集中在channel管道中,但在channe使用也比較容易出問題,比如:

import "time"func main() {ch := make(chan int)go func() {for i := 0; i < 1000; i++ {ch <- itime.Sleep(1)}}()go func() {close(ch)}()time.Sleep(100000)
}

踩坑分析:?在channel錯誤操作比較容易影響panic,下面幾類:a).向已關閉的channel發送數據導致panicb).重復關閉channel會導致panicc).關閉nil channel會導致panic因此在上面的例子就是向已關閉的channel發送數據導致panic,會導致程序不可用?如何避坑: channel關閉要適當,也不要向關閉的channel中進行操作,包括發送信息,再次關閉等

7.2?channel管道死鎖的問題

因為在channel存在生產者和消費者,也容易出現問題,比如:

 package mainfunc main() {ch := make(chan int)ch <- 1<-ch}
?

踩坑分析:?造成死鎖的原因:循環等待、資源共享、非搶占式,?在并發中出現通道死鎖有兩種情況:數據要發送,但是沒有人接收數據要接收,但是沒有人發送 因此上面就是,因為生產者和消費者在同一個goroutine中,因此無法并行執行,導致發送的消息一直無法被消費掉,而在ch<- 1一直阻塞著,出現死鎖。?如何避坑: 生產者和消費者不能屬于同一個goroutine,且生成者和消費者應該成對出現

8.sync同步機制panic問題

在并發下,sync同步機制也經常使用,也是比較容易出現問題的,比如,sync.Mutex:

package mainimport "sync"func main() {var r sync.Mutexr.Lock()r.Unlock()r.Unlock()
}
?

踩坑分析:?在同步機制上造成panic會有以下情況: a).sync.Mutex 沒有加鎖就進行解鎖而導致panic b).sync.Mutex 重復解鎖而導致panic c).sync.WaitGroup 計數為負而導致panic 因此上面就是,sync.Mutex 重復解鎖而導致panic?如何避坑: 加鎖和解鎖配對出現

9?select+timer

項目中在一些情況下需要進行超時控制,使用select+timer去解決超時控制,這邊也會有一個坑,比如:

package main
import ("fmt""time"
)func main() {ch := make(chan string)go func() {for i := 0; i < 100; i++ {ch <- "ok"}}()for {select {case v := <-ch:fmt.Println(v)case <-time.After(time.Second * 10):fmt.Println("timeout")}}
}
?

踩坑分析:?在for循環每次select的時候,都會實例化一個一個新的定時器。該定時器在10秒后 ,才會被激活,但是激活后已經跟select無引用關系,被gc給清理掉。 換句話說,被遺棄的time.After定時任務還是在時間堆里面,定時任務未到期之前,是不會被gc清理的。因此,會出現內存泄漏的現象。

如何避坑

a).改為timer的方式:

ticker := time.NewTicker(3 * time.Second)
for {<-ticker.Cfmt.Println("timeout")
}
?

b).使用context.WithTimeout方式:

ctx, cancel := context.WithTimeout(ctx, 3*time.Second)defer cancel() select {case <-ch:return truecase <-ctx.Done():return false
}
?

go?編輯程序員

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/13879.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13879.shtml
英文地址,請注明出處:http://en.pswp.cn/web/13879.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

安卓手機APP開發__網絡連接性支持VPN

安卓手機APP開發__網絡連接性支持VPN 安卓提供了API給開發者,來創建一個虛擬的私有網絡(VPN)的解決方案. 根據這里的介紹,你能知道如何開發和測試你的針對安卓設備的VPN的客戶端. 概述 VPN允許設備為了安全地連接網絡,而沒有物理性的連接在一個網絡上. 安卓包括了一個內嵌的…

【無重復字符的最長子串】python,滑動窗口+哈希表

滑動窗口哈希表 哈希表 seen 統計&#xff1a; 指針 j遍歷字符 s&#xff0c;哈希表統計字符 s[j]最后一次出現的索引 。 更新左指針 i &#xff1a; 根據上輪左指針 i 和 seen[s[j]]&#xff0c;每輪更新左邊界 i &#xff0c;保證區間 [i1,j] 內無重復字符且最大。 更新結…

使用JSDOM安全截斷文章HTML內容

在Web開發中&#xff0c;經常需要處理大量的HTML內容&#xff0c;尤其是在展示文章預覽、動態加載內容或限制顯示長度等場景中。直接截斷HTML字符串可能會導致頁面布局混亂、樣式錯誤或標簽不完整等問題。為了安全地截斷HTML內容&#xff0c;我們可以利用jsdom庫來解析HTML&…

JVM學習-垃圾回收器(一)

垃圾回收器 按線程數分類 串行垃圾回收器 串行回收是在同一時間段內只允許有一個CPU用于執行垃圾回收操作&#xff0c;此時工作線程被暫停&#xff0c;直至垃圾收集工作結束 在諸如單CPU處理器或者較小的應用內存等硬件平臺不是特別優越的場合&#xff0c;串行回收器的性能表…

http和https的區別,怎么免費實現https(內涵教學)

超文本傳輸協議HTTP協議被用于在Web瀏覽器和網站服務器之間傳遞信息&#xff0c;HTTP協議以明文方式發送內容&#xff0c;不提供任何方式的數據加密&#xff0c;如果攻擊者截取了Web瀏覽器和網站服務器之間的傳輸報文&#xff0c;就可以直接讀懂其中的信息&#xff0c;因此&…

etcd 和 MongoDB 的混沌(故障注入)測試方法

最近在對一些自建的數據庫 driver/client 基礎庫的健壯性做混沌&#xff08;故障&#xff09;測試, 去驗證了解業務的故障處理機制和恢復時長. 主要涉及到了 MongoDB 和 etcd 這兩個基礎組件. 本文會介紹下相關的測試方法. MongoDB 中的故障測試 MongoDB 是比較世界上熱門的文…

AI網絡爬蟲:批量爬取電視貓上面的《慶余年》分集劇情

電視貓上面有《慶余年》分集劇情&#xff0c;如何批量爬取下來呢&#xff1f; 先找到每集的鏈接地址&#xff0c;都在這個class"epipage clear"的div標簽里面的li標簽下面的a標簽里面&#xff1a; <a href"/drama/Yy0wHDA/episode">1</a> 這個…

速盾:負載均衡能防ddos攻擊嗎?

負載均衡是一種分布式系統的設計思想&#xff0c;通過將流量分散到多個服務器上&#xff0c;以提高系統的穩定性和可擴展性。然而&#xff0c;負載均衡本身并不能完全防止DDoS攻擊&#xff0c;但可以在一定程度上減輕其影響。 DDoS&#xff08;分布式拒絕服務&#xff09;攻擊…

【C語言】8.C語言操作符詳解(1)

文章目錄 1.操作符的分類2.?進制和進制轉換3.原碼、反碼、補碼4.移位操作符4.1 左移操作符4.2 右移操作符 5.位操作符&#xff1a;&、|、^、~5.1 &&#xff1a;按位與5.2 |&#xff1a;按位或5.3 ^&#xff1a;按位異或5.4 ~&#xff1a;按位取反5.5 例題例題1例題2例…

短視頻矩陣系統4年獨立開發正規代發布接口源碼搭建部署開發

1. 短視頻矩陣源碼技術開發要求及實現流程&#xff1a; 短視頻矩陣源碼開發要求具備視頻錄制、編輯、剪輯、分享等基本功能&#xff0c;支持實時濾鏡、特效、音樂等個性化編輯&#xff0c;能夠實現高效的視頻渲染和處理。開發流程主要包括需求分析、技術選型、設計架構、編碼實…

Web前端開發技術、詳細文章、(例子)html 列表、有序列表、無序列表、列表嵌套

目錄 列表概述 列表類型與標記符號 無序列表 語法&#xff1a; 語法說明&#xff1a; 無序列表標記的 type 屬性及其說明 代碼解釋 有序列表 基本語法 屬性說明 1、列表 o1標記的屬性 2、列表項li標記的屬性 有序列表 o1標記的屬性、值 代碼解釋 列表嵌套 基本…

如何將Qt pro工程文件 改成CMakeLists.txt

Qt pro工程管理文件&#xff0c;本人認為是很好用的&#xff0c;語法簡潔易懂&#xff0c;但是只能在QtCreator中使用&#xff0c;想用使用其它IDE比如Clion或者vs&#xff0c;CMakeLists是種通用的選擇&#xff0c;另外QtCreator的調試功能跟粑粑一樣。 一&#xff0c;思路 …

FreeBSD/Linux下的系統資源監視器排隊隊

bpytop bpytop 是一個基于 Python 的資源監視器&#xff0c;可以在 FreeBSD 上使用。它提供了對文件寫入磁盤、網絡、CPU 和內存占用的監視功能。 pkg install bpytop 或者用ports安裝 cd /usr/ports/sysutils/bpytop/ make install clean bashtop bashtop 也是一個基于 P…

化簡資源分配圖判斷是否發生死鎖

目錄 1.資源分配圖的概念 2.判斷是否發生死鎖 1.資源分配圖的概念 資源分配圖表示進程和資源之間的請求關系&#xff0c;例如下圖&#xff1a; P代表進程&#xff0c;R代表資源&#xff0c;R方框中 有幾個圓球就表示有幾個這種資源&#xff0c;在圖中&#xff0c;R1指向P1&a…

C++ RPC ORM 高速解析

支持所有常用編程語 https://capnproto.org/GitHub - capnproto/capnproto: Capn Proto serialization/RPC system - core tools and C library https://capnproto.org/capnproto-c-win32-1.0.2.zip 常用命令&#xff1a; capnp help capnp compile -oc myschema.capn…

java文件上傳時給pdf、word、excel、ppt、圖片添加水印

前言 在開發的過程中&#xff0c;因為文件的特殊性&#xff0c;需要給pdf、word、excel、ppt、圖片添加水印。添加水印可以在文件上傳時添加&#xff0c;也可以在文件下載時添加。因為業務的某些原因&#xff0c;文件需要在瀏覽器預覽&#xff0c;如果用戶將文件另存為則無法添…

算法與數據結構匯總

基本 數組 字符串 排序 矩陣 模擬 枚舉 字符串匹配 桶排序 計數排序 基數排序 回文&#xff1a;中心擴展 馬拉車 樹上啟發式合并 括號 數學表達式 字符串&#xff1a;前后綴分解。 貢獻法 分組&#xff1a; 【狀態機dp 狀態壓縮 分組】1994. 好子集的數目 【動態規劃】【前綴…

Excel中sum的跨表求和

#實際工作中&#xff0c;一個xlsx文件中會包含多個Excel表格&#xff0c;一般會有“總-分”的關系&#xff0c;如何把分表里的數字匯總到總表里呢&#xff1f; 一般有上圖所示的兩種表達方式。 可以使用通配符 *&#xff1a;代表任意個數、任意字符&#xff1b; &#xff1f;&…

51單片機的最小系統詳解

51單片機的最小系統詳解 1. 引言 在嵌入式系統中,51單片機被廣泛應用于各種小型控制器和嵌入式開發板中。相信很多人都接觸過51單片機,但是對于51單片機的最小系統卻了解得不夠深入。本文將從振蕩電路、電源模塊、復位電路、LED指示燈和調試接口五個方面詳細介紹51單片機的…

quartz定時任務

Quartz 數據結構 quartz采用完全二叉樹&#xff1a;除了最后一層每一層節點都是滿的&#xff0c;而且最后一層靠左排列。 二叉樹節點個數規則&#xff1a;每層從左開始&#xff0c;第一層只有一個&#xff0c;就是2的0次冪&#xff0c;第二層兩個就是2的1次冪&#xff0c;第三…