GO并發過高導致程序崩潰如何解決

#作者:曹付江

文章目錄

  • 1.并發過高導致程序崩潰
  • 2. 如何解決
    • 2.1 利用 channel 的緩存區
    • 2.2 利用第三方庫
  • 3 調整系統資源的上限
    • 3.1 ulimit
    • 3.2 虛擬內存(virtual memory)

1.并發過高導致程序崩潰

看一個非常簡單的例子:

func main() {var wg sync.WaitGroupfor i := 0; i < math.MaxInt32; i++ {wg.Add(1)go func(i int) {defer wg.Done()fmt.Println(i)time.Sleep(time.Second)}(i)}wg.Wait()
}

這個例子實現了 math.MaxInt32 個協程的并發,約 2^31 = 2 億個,每個協程內部幾乎沒有做什么事情。正常的情況下呢,這個程序會亂序輸出 1 -> 2^31 個數字。
那實際運行的結果是怎么樣的呢?

$ go run main.go
...
150577
150578
panic: too many concurrent operations on a single file or socket (max 1048575)goroutine 1199236 [running]:
internal/poll.(*fdMutex).rwlock(0xc0000620c0, 0x0, 0xc0000781b0)/usr/local/go/src/internal/poll/fd_mutex.go:147 +0x13f
internal/poll.(*FD).writeLock(...)/usr/local/go/src/internal/poll/fd_mutex.go:239
internal/poll.(*FD).Write(0xc0000620c0, 0xc125ccd6e0, 0x11, 0x20, 0x0, 0x0, 0x0)/usr/local/go/src/internal/poll/fd_unix.go:255 +0x5e
fmt.Fprintf(0x10ed3e0, 0xc00000e018, 0x10d3024, 0xc, 0xc0e69b87b0, 0x1, 0x1, 0x11, 0x0, 0x0)/usr/local/go/src/fmt/print.go:205 +0xa5
fmt.Printf(...)/usr/local/go/src/fmt/print.go:213
main.main.func1(0xc0000180b0, 0x124c31)

… 運行的結果是程序直接崩潰了,關鍵的報錯信息是:
panic: too many concurrent operations on a single file or socket (max 1048575)

對單個 file/socket 的并發操作個數超過了系統上限,這個報錯是 fmt.Printf 函數引起的,fmt.Printf 將格式化后的字符串打印到屏幕,即標準輸出。在 linux 系統中,標準輸出也可以視為文件,內核(kernel)利用文件描述符(file descriptor)來訪問文件,標準輸出的文件描述符為 1,錯誤輸出文件描述符為 2,標準輸入的文件描述符為 0。
簡而言之,系統的資源被耗盡了。
那如果我們將 fmt.Printf 這行代碼去掉呢?那程序很可能會因為內存不足而崩潰。這一點更好理解,每個協程至少需要消耗 2KB 的空間,那么假設計算機的內存是 2GB,那么至多允許 2GB/2KB = 1M 個協程同時存在。那如果協程中還存在著其他需要分配內存的操作,那么允許并發執行的協程將會數量級地減少。

2. 如何解決

不同的應用程序,消耗的資源是不一樣的。比較推薦的方式的是:應用程序來主動限制并發的協程數量

2.1 利用 channel 的緩存區

// main_chan.go
func main() {var wg sync.WaitGroupch := make(chan struct{}, 3)for i := 0; i < 10; i++ {ch <- struct{}{}wg.Add(1)go func(i int) {defer wg.Done()log.Println(i)time.Sleep(time.Second)<-ch}(i)}wg.Wait()
}
  • make(chan struct{}, 3) 創建緩沖區大小為 3 的 channel,在沒有被接收的情況下,至多發送 3 個消息則被阻塞。
  • 開啟協程前,調用 ch <- struct{}{},若緩存區滿,則阻塞。
  • 協程任務結束,調用 <-ch 釋放緩沖區。
  • sync.WaitGroup 并不是必須的,例如 http 服務,每個請求天然是并發的,此時使用 channel 控制并發處理的任務數量,就不需要 sync.WaitGroup。
    運行結果如下:
$ go run main_chan.go
2020/12/21 00:48:28 2
2020/12/21 00:48:28 0
2020/12/21 00:48:28 1
2020/12/21 00:48:29 3
2020/12/21 00:48:29 4
2020/12/21 00:48:29 5
2020/12/21 00:48:30 6
2020/12/21 00:48:30 7
2020/12/21 00:48:30 8
2020/12/21 00:48:31 9

從日志中可以很容易看到,每秒鐘只并發執行了 3 個任務,達到了協程并發控制的目的。

2.2 利用第三方庫

目前有很多第三方庫實現了協程池,可以很方便地用來控制協程的并發數量,比較受歡迎的有:

  • Jeffail/tunny
  • panjf2000/ants
    以 tunny 舉例:
package mainimport ("log""time""github.com/Jeffail/tunny"
)
func main() {pool := tunny.NewFunc(3, func(i interface{}) interface{} {log.Println(i)time.Sleep(time.Second)return nil})defer pool.Close()for i := 0; i < 10; i++ {go pool.Process(i)}time.Sleep(time.Second * 4)
}
  • tunny.NewFunc(3, f) 第一個參數是協程池的大小(poolSize),第二個參數是協程運行的函數(worker)。
  • pool.Process(i) 將參數 i 傳遞給協程池定義好的 worker 處理。
  • pool.Close() 關閉協程池。
    運行結果如下:
$ go run main_tunny.go
2020/12/21 01:00:21 6
2020/12/21 01:00:21 1
2020/12/21 01:00:21 3
2020/12/21 01:00:22 8
2020/12/21 01:00:22 4
2020/12/21 01:00:22 7
2020/12/21 01:00:23 5
2020/12/21 01:00:23 2
2020/12/21 01:00:23 0
2020/12/21 01:00:24 9

3 調整系統資源的上限

3.1 ulimit

有些場景下,即使我們有效地限制了協程的并發數量,但是仍舊出現了某一類資源不足的問題,例如:

  • too many open files
  • out of memory

例如分布式編譯加速工具,需要解析 gcc 命令以及依賴的源文件和頭文件,有些編譯命令依賴的頭文件可能有上百個,那這個時候即使我們將協程的并發數限制到 1000,也可能會超過進程運行時并發打開的文件句柄數量,但是分布式編譯工具,僅將依賴的源文件和頭文件分發到遠端機器執行,并不會消耗本機的內存和 CPU 資源,因此1000個并發并不高,這種情況下,降低并發數會影響編譯加速的效率,那能不能增加進程能同時打開的文件句柄數量呢?
操作系統通常會限制同時打開文件數量、棧空間大小等,ulimit -a 可以看到系統當前的設置:

$ ulimit -a
-t: cpu time (seconds)              unlimited
-f: file size (blocks)              unlimited
-d: data seg size (kbytes)          unlimited
-s: stack size (kbytes)             8192
-c: core file size (blocks)         0
-v: address space (kbytes)          unlimited
-l: locked-in-memory size (kbytes)  unlimited
-u: processes                       1418
-n: file descriptors                12800

我們可以使用 ulimit -n 999999,將同時打開的文件句柄數量調整為 999999 來解決這個問題,其他的參數也可以按需調整。

3.2 虛擬內存(virtual memory)

虛擬內存是一項非常常見的技術了,即在內存不足時,將磁盤映射為內存使用,比如 linux 下的交換分區(swap space)。
在 linux 上創建并使用交換分區是一件非常簡單的事情:
sudo fallocate -l 20G /mnt/.swapfile # 創建 20G 空文件
sudo mkswap /mnt/.swapfile # 轉換為交換分區文件
sudo chmod 600 /mnt/.swapfile # 修改權限為 600
sudo swapon /mnt/.swapfile # 激活交換分區
free -m # 查看當前內存使用情況(包括交換分區)
關閉交換分區也非常簡單:

sudo swapoff /mnt/.swapfile
rm -rf /mnt/.swapfile

磁盤的 I/O 讀寫性能和內存條相差是非常大的,例如 DDR3 的內存條讀寫速率很容易達到 20GB/s,但是 SSD 固態硬盤的讀寫性能通常只能達到 0.5GB/s,相差 40倍之多。因此,使用虛擬內存技術將硬盤映射為內存使用,顯然會對性能產生一定的影響。如果應用程序只是在較短的時間內需要較大的內存,那么虛擬內存能夠有效避免 out of memory 的問題。如果應用程序長期高頻度讀寫大量內存,那么虛擬內存對性能的影響就比較明顯了。

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

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

相關文章

Linux -- gdb/cgdb的認識和使用

預備知識 程序的發布?式有兩種&#xff0c; debug 模式和 release 模式&#xff0c; Linux gcc/g 出來的?進制程 序&#xff0c;默認是 release 模式。 要使?gdb調試&#xff0c;必須在源代碼?成?進制程序的時候, 加上 -g 選項&#xff0c;如果沒有添加&#x…

window 顯示驅動開發-Direct3D 呈現性能改進(四)

調用資源創建、映射和取消映射函數的行為更改 對于 WDDM 1.3 及更高版本驅動程序實現的這些函數&#xff0c;Direct3D 運行時為映射默認方案提供一組受限的輸入值。 這些受限值僅適用于支持功能級別 11.1 及更高版本的驅動程序。 CreateResource (D3D11) 函數— 這些輸入 D3…

3.python操作mysql數據庫

前言&#xff1a;在現代應用程序中&#xff0c;數據庫扮演者至關重要的角色。mysql是一個流行的關系型數據庫管理系統&#xff0c;廣泛應用于各種規模的應用中。在pytho中&#xff0c;我們可以通過連接庫與mysql數據庫進行交互&#xff0c;實現數據的增刪改查操作。與此同時&am…

day023-網絡基礎與OSI七層模型

文章目錄 1. 網絡基礎知識點1.1 網絡中的單位1.2 查看實時網速&#xff1a;iftop1.3 交換機、路由器 2. 路由表2.1 查看路由表的命令2.2 路由追蹤命令 3. 通用網站網絡架構4. 局域網上網原理-NAT5. 虛擬機上網原理6. 虛擬機的網絡模式6.1 NAT模式6.2 橋接模式6.3 僅主機模式 7.…

DeepSeek智能對話助手項目

目錄&#xff1a; 1、效果圖2、實現代碼3、溫度和TopK的作用對比 1、效果圖 2、實現代碼 # import gradio as gr# def reverse_text(text): # return text[::-1]# demogr.Interface(fnreverse_text,inputs"text",outputs"text")# demo.launch(share&q…

視覺中國:鏡頭下的中國發展圖景

2025年5月下旬&#xff0c;從北國草原到江南水鄉&#xff0c;從文化遺產到科技創新&#xff0c;中國大地上演著一幕幕生機勃勃的圖景。河北張家口的沙狐幼崽與濕地生態和諧共生&#xff0c;湖北襄陽的茶園雕琢出詩意田園&#xff1b;北京殷商文創的活力、沈陽文物情景劇的創意&…

LabVIEW 中內存釋放相關問題

在LabVIEW 編程領域&#xff0c;內存管理是一個關鍵且復雜的議題。我們常常關注 LabVIEW 如何將內存釋放回操作系統&#xff08;OS&#xff09;&#xff0c;以及是否有方法確保在特定數據結構&#xff08;如隊列、變體屬性、動態數據引用 DVR 等&#xff09;銷毀、刪除或清空后…

基于正點原子阿波羅F429開發板的LWIP應用(4)——HTTP Server功能

說在開頭 正點原子F429開發板主芯片采用的是STM32F429IGT6&#xff0c;網絡PHY芯片采用的是LAN8720A(V1)和YT8512C(V2)&#xff0c;采用的是RMII連接&#xff0c;PHY_ADDR為0&#xff1b;在代碼中將會對不同的芯片做出適配。 CubeMX版本&#xff1a;6.6.1&#xff1b; F4芯片組…

設計模式-結構型模式(詳解)

適配器模式 將一個類的接口轉換成客戶端期望的另一個接口&#xff0c;解決接口不兼容問題。 適配器模式由四部分組成&#xff1a; 客戶端&#xff1a;即需要使用目標接口的類 目標接口 需要適配的類&#xff0c;也就是已經存在好的功能&#xff0c;但客戶端通過目標接口沒辦…

銀河麒麟操作系統下載

產品試用申請國產操作系統、麒麟操作系統——麒麟軟件官方網站 下載頁面鏈接如上&#xff0c;申請試用即可。 申請試用填寫后提交&#xff0c;界面就變成了這樣&#xff0c;可以挑選適合自己的版本。 海思麒麟9006C版&#xff0c;如下&#xff1a; 本地下載&#xff1a;Kylin…

[CARLA系列--03]如何打包生成CARLA 0.9.15的非編輯版(地圖的加載與卸載)

前兩篇文章介紹了如何去安裝可編輯版的CARLA 0.9.15&#xff0c;這個完整的工程文件實在是太大了&#xff0c;大概消耗了100個G的磁盤空間&#xff0c;當在進行一個CARLA項目的時候&#xff0c;不利于在每個開發電腦都去安裝部署一套CARLA 0.9.15的源碼&#xff0c;所以把自己這…

【機器學習基礎】機器學習入門核心算法:樸素貝葉斯(Naive Bayes)

機器學習入門核心算法&#xff1a;樸素貝葉斯&#xff08;Naive Bayes&#xff09;&#xff09; 一、算法邏輯1.1 基本概念1.2 基本流程 二、算法原理與數學推導2.1 貝葉斯定理2.2 樸素貝葉斯分類器2.3 不同分布假設下的概率計算2.3.1 高斯樸素貝葉斯&#xff08;連續特征&…

云服務器系統盤滿了,但是其他正常,是否可能是被攻擊了

目錄 問題背景分析解決系統盤滿的問題解決結果 問題背景 今天登錄我的云服務器看了眼&#xff0c;發現系統盤滿了&#xff0c;但是其他正常 分析 1、首先要確認是否是被攻擊&#xff1a; top / htop (安裝&#xff1a;yum install htop 或 apt install htop)&#xff1a;…

雙因子COX 交互 共線性 -spss

SPSS 簡要界面操作步驟(針對雙因子 COX 分析) 1. 數據準備 變量格式:確保數據已整理為以下格式(示例): 時間變量(如 Time_to_Recurrence)結局變量(如 Recurrence:1=復發,0=未復發)預測變量(CSPG4_HSCORE、FAM49B_Status 二分類變量)協變量(如 Lesion_Size、Pat…

【MySQL】第12節|MySQL 8.0 主從復制原理分析與實戰(二)

一、組復制&#xff08;MGR&#xff09;核心概念 1. 定義與定位 目標&#xff1a;解決傳統主從復制的單點故障、數據不一致問題&#xff0c;提供高可用、高擴展的分布式數據庫方案。基于 GTID&#xff1a;依賴全局事務標識符&#xff08;GTID&#xff09;實現事務一致性&…

React 泛型組件:用TS來打造靈活的組件。

文章目錄 前言一、什么是泛型組件&#xff1f;二、為什么需要泛型組件&#xff1f;三、如何在 React 中定義泛型組件&#xff1f;基礎泛型組件示例使用泛型組件 四、泛型組件的高級用法帶默認類型的泛型組件多個泛型參數 五、泛型組件的實際應用場景數據展示組件表單組件狀態管…

如何手搓一個查詢天氣的mcp server

環境配置煩請移步上一篇博客 這里直接步入主題&#xff0c;天氣查詢的api用的是openweather&#xff0c;免費注冊就可以使用了 每天1000次內使用時免費的&#xff0c;大概的api 如下 https://api.openweathermap.org/data/2.5/weather?qBeijing,cn&APPID注冊后可以拿到一個…

深入解析計算機網絡核心協議:ARP、DHCP、DNS與HTTP

文章目錄 一、ARP&#xff08;地址解析協議&#xff09;1.1 定義與功能1.2 工作原理1.3 應用場景1.4 安全風險與防御 二、DHCP&#xff08;動態主機配置協議&#xff09;2.1 定義與功能2.2 工作原理2.3 應用場景2.4 優缺點與安全建議 三、DNS&#xff08;域名系統&#xff09;3…

《Java 單例模式:從類加載機制到高并發設計的深度技術剖析》

【作者簡介】“琢磨先生”--資深系統架構師、985高校計算機碩士&#xff0c;長期從事大中型軟件開發和技術研究&#xff0c;每天分享Java硬核知識和主流工程技術&#xff0c;歡迎點贊收藏&#xff01; 一、單例模式的核心概念與設計目標 在軟件開發中&#xff0c;我們經常會遇…

NL2SQL代表,Vanna

Vanna 核心功能、應用場景與技術特性詳解 一、核心功能 1. 自然語言轉SQL查詢 Vanna 允許用戶通過自然語言提問&#xff08;如“顯示2024年銷售額最高的產品”&#xff09;&#xff0c;自動生成符合數據庫規范的SQL查詢語句。其底層采用 RAG&#xff08;檢索增強生成&#xf…