Golang 併發限制和超時限制
我們先來看一個簡單的併發例項:
函式run()接受輸入的引數,sleep若干秒。然後通過go關鍵字併發執行,通過channel返回結果。package main import ( "fmt" "time" ) func run(task_id, sleeptime int, ch chan string) { time.Sleep(time.Duration(sleeptime) * time.Second) ch <- fmt.Sprintf("task id %d , sleep %d second", task_id, sleeptime) return } func main() { input := []int{3, 2, 1} ch := make(chan string) startTime := time.Now() fmt.Println("Multirun start") for i, sleeptime := range input { go run(i, sleeptime, ch) } for range input { fmt.Println(<-ch) } endTime := time.Now() fmt.Printf("Multissh finished. Process time %s. Number of tasks is %d", endTime.Sub(startTime), len(input)) }
channel就是goroutine之間通訊的“管道”。管道中的資料流通,實際上是goroutine之間的一種記憶體共享。我們通過它可以在goroutine之間互動資料。
ch <- xxx //向channel寫入資料
<- ch //從channel中讀取資料
channel分為無緩衝(unbuffered)和緩衝(buffered)兩種。例如剛才我們通過如下方式建立一個無緩衝的channel。
ch := make(chan string)
輸出結果:
Multirun start
task id 2 , sleep 1 second
task id 1 , sleep 2 second
task id 0 , sleep 3 second
Multissh finished. Process time 3s. Number of tasks is 3
Program exited.
三個goroutine分別sleep了3,2,1秒。但總耗時只有3秒。所以併發生效了,go的併發就是這麼簡單。
執行結果,現在輸出的次序和輸入的次序是一致的,package main import ( "fmt" "time" ) func run(task_id, sleeptime int, ch chan string) { time.Sleep(time.Duration(sleeptime) * time.Second) ch <- fmt.Sprintf("task id %d , sleep %d second", task_id, sleeptime) return } func main() { input := []int{3, 2, 1} chs := make([]chan string, len(input)) startTime := time.Now() fmt.Println("Multirun start") for i, sleeptime := range input { chs[i] = make(chan string) go run(i, sleeptime, chs[i]) } for _, ch := range chs { fmt.Println(<-ch) } endTime := time.Now() fmt.Printf("Multissh finished. Process time %s. Number of tasks is %d", endTime.Sub(startTime), len(input)) }
Multirun start
task id 0 , sleep 3 second
task id 1 , sleep 2 second
task id 2 , sleep 1 second
Multissh finished. Process time 3s. Number of tasks is 3
Program exited.
超時控制
如果某個goroutine執行時間太長了,那肯定會拖累主gorotine被阻塞住,整個程式就掛起在那兒了。因此我們需要有超時的控制。
通常我們可以通過select + time.After來進行超時檢查,例如這樣,我們增加一個函式Run(),在Run()中執行go run()。並通過select + time.After進行超時判斷。
package main
import (
"fmt"
"time"
)
func Run(task_id, sleeptime, timeout int, ch chan string) {
ch_run := make(chan string)
go run(task_id, sleeptime, ch_run)
select {
case re := <-ch_run:
ch <- re
case <-time.After(time.Duration(timeout) * time.Second):
re := fmt.Sprintf("task id %d , timeout", task_id)
ch <- re
}
}
func run(task_id, sleeptime int, ch chan string) {
time.Sleep(time.Duration(sleeptime) * time.Second)
ch <- fmt.Sprintf("task id %d , sleep %d second", task_id, sleeptime)
return
}
func main() {
input := []int{3, 2, 1}
timeout := 2
chs := make([]chan string, len(input))
startTime := time.Now()
fmt.Println("Multirun start")
for i, sleeptime := range input {
chs[i] = make(chan string)
go Run(i, sleeptime, timeout, chs[i])
}
for _, ch := range chs {
fmt.Println(<-ch)
}
endTime := time.Now()
fmt.Printf("Multissh finished. Process time %s. Number of task is %d", endTime.Sub(startTime), len(input))
}
執行結果,task 0 和task 1已然超時Multirun start
task id 0 , timeout
task id 1 , timeout
tasi id 2 , sleep 1 second
Multissh finished. Process time 2s. Number of task is 3
Program exited.
併發限制
如果任務資料太多,不加以限制的併發開啟goroutine的話,可能會過多的佔用資源,伺服器可能會爆炸。所以實際環境中併發限制也是一定要做的。
一種常見的做法就是利用channel的緩衝機制,我們分別建立一個帶緩衝和不帶緩衝的channel看看
ch := make(chan string) // 這是一個無緩衝的 channel,或者說緩衝區長度是 0
ch := make(chan string, 1) // 這是一個帶緩衝的 channel, 緩衝區長度是 1
這兩者的區別在於,如果channel沒有緩衝,或者緩衝滿了。goroutine就會自動阻塞,直到channel裡的資料被讀取為止。舉個例子:
package main
import (
"fmt"
)
func main() {
ch := make(chan string)
ch <- "123"
fmt.Println(<-ch)
}
這代程式碼執行將報錯atal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox531498664/main.go:9 +0x60
Program exited.
這是因為我們建立的ch是一個無緩衝的channel。因此在執行到ch <- "123",這個gorountine就阻塞了,後面的fmt.Printfln(<-ch)沒有辦法得到執行。所以將會包deadlock錯誤。如果我們改成這樣,程式就可以執行
package main
import (
"fmt"
)
func main() {
ch := make(chan string, 1)
ch <- "123"
fmt.Println(<-ch)
}
執行結果:123如果我們改成這樣
package main
import (
"fmt"
)
func main() {
ch := make(chan string, 1)
ch <- "123"
ch <- "123"
fmt.Println(<-ch)
fmt.Println(<-ch)
}
儘管讀取了兩次 channel,但是程式還是會死鎖,因為緩衝區滿了,goroutine 阻塞掛起。第二個 ch<- "123" 是沒有辦法寫入的。fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox642690323/main.go:10 +0x80
Program exited.
因此,利用channel的緩衝設定,我們就可以來實現併發的限制。我們只要在執行併發的同時,往一個帶有緩衝的channel裡寫入點東西。讓併發的gorountine在執行完成後把這個channel裡的東西給讀走。這樣整個併發的數量就講控制在這個channel的緩衝區大小上。比如我們可以用一個bool型別的帶緩衝的channel作為併發限制的計數器。
chLimit := make(chan bool, 1);
然後在併發執行的地方,每建立一個新的goroutine,都往chLimit裡塞個東西。
chLimit := make(chan bool, 1);
然後在併發執行的地方,每建立新的goroutine,都往chLimit裡塞個東西。
for i, sleeptime := range input {
chs[i] = make(chan string, 1)
chLimit <- true
go limitFunc(chLimit, chs[i], i, sleeptime, timeout)
}
這裡通過go關鍵字併發執行的是新構造的函式。他在執行完原來的Run()後,會把chLimit的緩衝裡給消費掉一個。limitFunc := func(chLimit chan bool, ch chan string, task_id, sleeptime, timeout int) {
Run(task_id, sleeptime, timeout, ch)
<-chLimit
}
這樣一來,當建立的goroutine數量到達chLimit的緩衝區上限後。主goroutine就掛起阻塞了,直到這些gorountine執行完畢,消費掉chLimit緩衝區的資料,程式才會繼續建立新的goroutine。我們併發數量限制的目的也就達到了。以下是完整程式碼:
package main
import (
"fmt"
"time"
)
func Run(task_id, sleeptime, timeout int, ch chan string) {
ch_run := make(chan string)
go run(task_id, sleeptime, ch_run)
select {
case re := <-ch_run:
ch <- re
case <-time.After(time.Duration(timeout) * time.Second):
re := fmt.Sprintf("task id %d , timeout", task_id)
ch <- re
}
}
func run(task_id, sleeptime int, ch chan string) {
time.Sleep(time.Duration(sleeptime) * time.Second)
ch <- fmt.Sprintf("task id %d , sleep %d second", task_id, sleeptime)
return
}
func main() {
input := []int{3, 2, 1}
timeout := 2
chLimit := make(chan bool, 1)
chs := make([]chan string, len(input))
limitFunc := func(chLimit chan bool, ch chan string, task_id, sleeptime, timeout int) {
Run(task_id, sleeptime, timeout, ch)
<-chLimit
}
startTime := time.Now()
fmt.Println("Multirun start")
for i, sleeptime := range input {
chs[i] = make(chan string, 1)
chLimit <- true
go limitFunc(chLimit, chs[i], i, sleeptime, timeout)
}
for _, ch := range chs {
fmt.Println(<-ch)
}
endTime := time.Now()
fmt.Printf("Multissh finished. Process time %s. Number of task is %d", endTime.Sub(startTime), len(input))
}
執行結果Multirun start
task id 0 , timeout
task id 1 , timeout
task id 2 , sleep 1 second
Multissh finished. Process time 5s. Number of task is 3
Program exited.
chLimit的緩衝是1.task 0和task 1耗時2秒超時。task2耗時1秒。總耗時5秒。併發限制生效了。如果我們修改併發限制為 2
chLimit := make(chan bool, 2)
執行結果
Multirun start
task id 0 , timeout
task id 1 , timeout
task id 2 , sleep 1 second
Multissh finished. Process time 3s. Number of task is 3
Program exited.
相關推薦
Golang 併發限制和超時限制
我們先來看一個簡單的併發例項:package main import ( "fmt" "time" ) func run(task_id, sleeptime int, ch chan string) { time.Sleep(time.D
資源的軟限制和硬限制
AIX 使用者使用的系統資源限制包括兩個概念 --- 硬限制(hard limits) 和軟限制(soft limits)。 hard limits自AIX 4.1版本開始引入。hard limits 應由AIX系統管理員設定,只有security組的成員可以將此值增大,使用者本身可以減小此限定值,但是其更改
Linux 硬限制和軟限制 http://www.cppblog.com/API/archive/2012/03/19/168289.html
這一部分的提出是為了防止失控的程序破壞系統的效能 nuix和Linux跟蹤程序使用的大部分資源,允許使用者和系統管理員使用對程序的資源限制 設定的限制有兩種: 硬限制和軟限制 硬限制是可以在任何時候任何程序中設定 但硬限制只能由超級使用者提起 軟限制是核心實際執行的限制,任何程序都可以將軟限制設定為任意
golang定時器和超時的使用
直接程式碼 func main() { var a chan string a =make(chan string) go sendDataTo(a) go timin
iOS之UITextField對輸入的內容的長度限制和內容限制
這裡講解兩個對文字框輸入內容的長度的限制: 首先是通過新增事件的方式來限制: - (void)viewDidLoad { [super viewDidLoad]; //建立文
nginx 限制ip併發數和限制速度
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Linux下的socket程式設計實踐(八) Select的限制和poll(併發的初步知識)
select的限制 用select實現的併發伺服器,能達到的併發數一般受兩方面限制: 1)一個程序能開啟的最大檔案描述符限制。這可以通過調整核心引數來改變。可以通過ulimit -n(number)來調整或者使用setrlimit函式設定(需要root許可權),但一個系
利用apache限制IP併發數和下載流量控制
一,為什麼要對IP併發數,下載流量進行控制 說正題之前,先給大家講個故事,那是在2007年,我進了一家公司,當時我們正在給達芙妮做電子商務網,www.idaphne.com。從三月份開始做的吧,九月份正式上線,上線後沒多長時間,有一天伺服器老是死,後來查了一下,一個IP
Apache限制IP併發數和流量控制
轉自:http://www.cszhi.com/20120512/apache-mod_bandwidth-mod_limitipconn.html 使用mod_limitipconn模組限制IP併發連線數 安裝: wget http://dominia.org/dja
Golang gRPC學習(04): Deadlines超時限制
## 為什麼要使用Deadlines 當我們使用gRPC時,gRPC庫關係的是連線,序列化,反序列化和超時執行。Deadlines 允許gRPC客戶端設定自己等待多長時間來完成rpc操作,直到出現這個錯誤 `DEADLINE_EXCEEDED`。但是在正常情況下,這個DEADLINE_EXCEEDED預設設定
郵件發送和接收限制
郵件服務器 郵件系統 收發郵件 連接器 影響 郵件發送和接收限制禁止大郵件的作用: 限制用戶發送和接收郵件大小,能夠避免因為提交體積過大的郵件導致郵件服務器負載急增,造成服務器的過載而影響整個郵件系統。此外,限制接收郵件的大小,更關鍵的作用是避免外部郵件系統惡意發送大體積郵件對郵
Yii2框架RESTful API教程(二) - 格式化響應,授權認證和速率限制
formatter loginurl 而不是 filters ica cat 表示 程序 ssi 之前寫過一篇Yii2框架RESTful API教程(一) - 快速入門,今天接著來探究一下Yii2 RESTful的格式化響應,授權認證和速率限制三個部分 一、目錄結構 先
EditText實現輸入限制和校驗
code edit actor digi click pro reat tco example EditText實現輸入限制和校驗 一、方法 1)輸入限制 1、通過android:digits限制只能輸入小寫abc android:digits="abc" 2、通過an
Linux的企業-Cgconfig簡介和相關限制
cgconfig 簡介 相關限制 一.簡介 Cgroups是control groups的縮寫,是Linux內核提供的一種可以限制、記錄、隔離進程組(process groups)所使用的物理資源(如:cpu,memory,IO等等)的機制。最初由google的工程師提出,後來被整合進
MariaDB ColumnStore一些限制和BUG總結
mpp columnstore 限制1、不支持CHARACTER SET語法MariaDB [test]> create table t1(id int,name varchar(10) CHARACTER SET utf8)-> engine=Columnstore;ERROR 1178
HBase流量限制和表負載均衡剖析
hba https 均衡 des 間接 ges log4j hmaster 內容 1.概述 在HBase-1.1.0之前,HBase集群中資源都是全量的。用戶、表這些都是沒有限制的,看似完美實則隱患較大。今天,筆者就給大家剖析一下HBase的流量限制和表的負載均衡。
由於 Web 服務器上的“ISAPI 和 CGI 限制”列表設置,無法提供您請求的頁面
not found image data sre win png soft .exe gii 有時候IIS會遇到這種問題: HTTP 錯誤 404.2 - Not Found 由於 Web 服務器上的“ISAPI 和 CGI 限制”列表設置,無法
nginx和apache限制IP地址訪問的設置方法
nginx和apache限制IP地址一、nginx禁止IP地址訪問1、在nginx配置文件中加入這個:2、重啟nginx服務二、apache禁止IP地址訪問1、更改vhosts.conf文件:NameVirtualHost 192.168.1.191 <VirtualHost 192.168.1.191
IIS+PHP上傳文件大小限制和上傳時間限制,iis7和iis8上傳文件大小限制和上傳時間限制
img out 正常 .aspx log 指定 fig文件 .html span 先說IIS這邊的配置 一:點擊站點“管理”下的“配置編輯器”。
spatialite資料庫在移動端的使用---空間索引和觸發器限制
spatailite資料庫是對sqlite資料庫的擴充套件,增加了對空間屬性geometry的支援。shp資料可以匯入到spatialite資料庫中,用於移動端空間資料的儲存。