1. 程式人生 > >Golang 併發限制和超時限制

Golang 併發限制和超時限制

   我們先來看一個簡單的併發例項:

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))
}
  函式run()接受輸入的引數,sleep若干秒。然後通過go關鍵字併發執行,通過channel返回結果

  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 限制”列表設置,無法

nginxapache限制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上傳文件大小限制上傳時間限制,iis7iis8上傳文件大小限制上傳時間限制

img out 正常 .aspx log 指定 fig文件 .html span 先說IIS這邊的配置 一:點擊站點“管理”下的“配置編輯器”。

spatialite資料庫在移動端的使用---空間索引觸發器限制

      spatailite資料庫是對sqlite資料庫的擴充套件,增加了對空間屬性geometry的支援。shp資料可以匯入到spatialite資料庫中,用於移動端空間資料的儲存。