有關Golang channel關閉的優雅方式

2021-09-16 13:20:26 字數 4363 閱讀 7112

今天看到了一篇很不錯的文章,讀完之後我簡單重寫了示例**,文字內容也不一致,有興趣的同學可以去讀一下原文或譯文。

golang面試有關channel的問題肯可能會問到:

q:如何關閉乙個channel

a:close()

q:怎麼判斷乙個channel已經關閉了

a:(1) value, ok := <- ch // ok為false,則通道已經關閉; (2) <-ch // 如果channel關閉,這裡就不會阻塞,而這句**可以用在select中或直接在執行**中作為阻塞機制。

而在實際工作中,我也曾遇到過乙個專案的bug,根因就是關閉了已經關閉的channel,而出現了panic。

於是應該考慮使用一種機制,當多個goroutine使用乙個channel進行傳送或接收工作的時候,保證在goroutine中只執行一次關閉channel的操作。

當以多個goroutine的方式執行下面的兩個函式,顯然在第乙個safeclose()的goroutine執行便會關閉通道ch,而第二個safeclose()的goroutine執行便會出現panic。於是這裡通過recover()的方式來避免panic。

簡單來說,就是忽視關閉已經關閉的channel而產生的panic。顯然,這不是一種好的方法。

func

safesend

(ch chan

int, value int

)(closed bool)}

()ch <- value

return

false

}func

safeclose

(ch chan

int)

(justclosed bool)}

()close

(ch)

return

true

}

下面的方法是通過使用到sync.once,來保證關閉channel的操作只執行一次。使用的方式沒什麼毛病,在諸多golang開源專案中,大多使用sync.once來關閉channel。

type mychannel struct

func

newmychannel()

*mychannel

}func

(mc *mychannel)

safeclose()

)}

下面的例子是通過加鎖和設定關閉標誌,在鎖中執行通道關閉和對關閉標誌賦值,從而保證channel在初始關閉標誌狀態下只被關閉一次。

type mychannel1 struct

func

newmychannel1()

*mychannel

}func

(mc *mychannel1)

safeclose()

mc.mutex.

unlock()

}func

(mc *mychannel1)

isclosed()

bool

the channel closing principle(通道關閉原則)

1、乙個傳送端,多個接收端

當只有乙個傳送端,那麼在需要結束的時候,在傳送端直接關閉channel即可,示例如下:

import

("sync"

"fmt"

"math/rand"

"time"

)// sender: 1, receivers: m

const

( maxrandomnumber =

100000

numreceivers1 =

100)

var wg sync.waitgroup

func

sender

(ch chan

int)

else}}

func

receiver

(ch chan

int)

}func

main()

wg.wait()

}

2、多個傳送端,乙個接收端

當傳送端為多個的時候,為了避免多次關閉channel,可以考慮增加乙個作為標誌的channel,當需要關閉channel的時候,通過關閉標誌channel,通知多個傳送端結束工作從而停止了傳送工作。

換乙個角度去想,就標誌channel而言,唯一的接收端是它的實際傳送者,因此依然遵循通道關閉原則。所以實際上,傳輸資料的channel並沒有顯式關閉。示例**如下:

import

("math/rand"

"sync"

"fmt"

"time"

)const

( maxrandomnumber1 =

100000

numsender1 =

100)

var wg1 sync.waitgroup

func

sender1

(ch chan

int, stopch chan

struct)}

}func

receiver1

(ch chan

int, stopch chan

struct

) fmt.

println

(value)}}

func

main()

)for i:=

0;iwg1.

add(1)

goreceiver1

(ch, stopch)

wg1.

wait()

}

3、多個傳送端,多個接收端

當有多個傳送端和多個接收端時,無法再套用之前的方法。因為關閉channel的操作只能執行一次,之前當接收端是乙個的時候,是把接收端創造成乙個傳送端,那麼現在的情況,就可以考慮重新創造乙個「傳送端」,來做關閉通道的工作。

傳送端和接收端都有終止channel通訊的條件,當隨機值為0時傳送端終止,當隨機值為99999時接收端終止。在沒有達到條件的時候,moderator的goroutine中,tostop通道被阻塞,當有傳送端或接收端向tostop中傳值之後,stopch通道的關閉接著執行,然後所有傳送端和接收端都直接返回。同樣,資料通道ch沒有被顯式關閉。示例**如下:

import

("sync"

"math/rand"

"fmt"

"time"

)const

( maxrandomnumber2 =

100000

numsenders =

1000

numreceivers =10)

var(

wg2 sync.waitgroup

stoppedby string

)func

moderator

(stopch chan

struct

, tostop chan

string

)func

senders

(id int

, stopch chan

struct

, tostop chan

string

, ch chan

int)

return

}select}}

func

receivers

(id int

, stopch chan

struct

, tostop chan

string

, ch chan

int)

return

} fmt.

println

(value)

default:}

}}func

main()

) tostop :=

make

(chan

string,1

)gomoderator

(stopch, tostop)

for i :=

0; i < numsenders; i++

wg2.

add(numreceivers)

for i :=

0; i < numreceivers; i++

wg2.

wait()

fmt.

printf

("stopped by %s\n"

, stoppedby)

}

golang channel基本操作

channel可以實現執行緒的阻塞。建立無緩衝區channel,只能存放乙個值。var ch make chan int 建立有緩衝區channel,可以存放多個值,值到達上限才會阻塞。var ch1 make chan int,3 賦值 ch 555 取值 ch 關閉chnneal,關閉後無法在使...

深入學習golang channel

網路,併發 是go語言的兩大feature。go語言號稱 網際網路的c語言 與使用傳統的c語言相比,寫乙個server所使用的 更少,也更簡單。寫乙個server除了網路,另外就是併發,相對python等其它語言,go對併發支援使得它有更好的效能。goroutine和channel是go在 併發 方...

Golang channel 使用總結(一)

package main import fmt func main hbhly 56 128 demo go run g.go fatal error all goroutines are asleep deadlock goroutine 1 chan send main.main search ...