3 回答

TA貢獻1780條經驗 獲得超1個贊
您可以使用上下文在兩個 go 例程之間進行通信。例如,
package main
import (
"context"
"sync"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
wg := sync.WaitGroup{}
wg.Add(3)
go func() {
defer wg.Done()
for {
select {
// msg from other goroutine finish
case <-ctx.Done():
// end
}
}
}()
go func() {
defer wg.Done()
for {
select {
// msg from other goroutine finish
case <-ctx.Done():
// end
}
}
}()
go func() {
defer wg.Done()
// your operation
// call cancel when this goroutine ends
cancel()
}()
wg.Wait()
}

TA貢獻1875條經驗 獲得超5個贊
在通道上使用 close 表示完成。這允許多個 goroutine 通過在通道上接收來檢查完成情況。
每個 goroutine 使用一個通道來表示 goroutine 的完成。
done1 := make(chan struct{}) // closed when goroutine 1 returns
done2 := make(chan struct{}) // closed when goroutine 2 returns
go func() {
defer close(done1)
timer1 := time.NewTicker(1 * time.Second)
defer timer1.Stop()
timer2 := time.NewTicker(2 * time.Second)
defer timer2.Stop()
for {
select {
case <-done2:
// The other goroutine returned.
fmt.Println("done func 1")
return
case <-timer1.C:
fmt.Println("timer1 func 1")
case <-timer2.C:
fmt.Println("timer2 func 1")
return
}
}
}()
go func() {
defer close(done2)
for {
select {
case <-done1:
// The other goroutine returned.
fmt.Println("done func 2")
return
default:
time.Sleep(3 * time.Second)
fmt.Println("sleep done from func 2")
return
}
}
}()
fmt.Println("waiting for goroutines to complete")
// Wait for both goroutines to return. The order that
// we wait here does not matter.
<-done1
<-done2
fmt.Println("all done")

TA貢獻1846條經驗 獲得超7個贊
首先將等待 go-routines 和donechannel 分開。
使用 async.WaitGroup來協調 goroutine。
func main() {
wait := &sync.WaitGroup{}
N := 3
wait.Add(N)
for i := 1; i <= N; i++ {
go goFunc(wait, i, true)
}
wait.Wait()
fmt.Println(`Exiting main`)
}
每個 goroutine 將如下所示:
// code for the actual goroutine
func goFunc(wait *sync.WaitGroup, i int, closer bool) {
defer wait.Done()
defer fmt.Println(`Exiting `, i)
T := time.Tick(time.Duration(100*i) * time.Millisecond)
for {
select {
case <-T:
fmt.Println(`Tick `, i)
if closer {
return
}
}
}
}
(https://play.golang.org/p/mDO4P56lzBU)
我們的 main 函數在退出之前成功地等待 goroutines 退出。每個 goroutine 都在關閉自己,我們想要一種同時取消所有 goroutine 的方法。
我們將使用chan, 并利用從頻道接收的這一特性:
引用:關閉通道上的接收操作總是可以立即進行,在接收到任何先前發送的值之后產生元素類型的零值。(https://golang.org/ref/spec#Receive_operator)
我們修改我們的 goroutine 來檢查 CLOSE:
func goFunc(wait *sync.WaitGroup, i int, closer bool, CLOSE chan struct{}) {
defer wait.Done()
defer fmt.Println(`Exiting `, i)
T := time.Tick(time.Duration(100*i) * time.Millisecond)
for {
select {
case <-CLOSE:
return
case <-T:
fmt.Println(`Tick `, i)
if closer {
close(CLOSE)
}
}
}
}
然后我們改變我們的func main,讓它通過 CLOSE 通道,我們將設置closer變量,以便只有我們的最后一個 goroutine 會觸發關閉:
func main() {
wait := &sync.WaitGroup{}
N := 3
CLOSE := make(chan struct{})
// Launch the goroutines
wait.Add(N)
for i := 1; i <= N; i++ {
go goFunc(wait, i, i == N, CLOSE)
}
// Wait for the goroutines to finish
wait.Wait()
fmt.Println(`Exiting main`)
}
(https://play.golang.org/p/E91CtRAHDp2)
現在看起來一切正常。
但事實并非如此。并發很難。這段代碼中潛伏著一個錯誤,正等著在生產中咬你。讓我們浮出水面。
更改我們的示例,以便每個goroutine 都將關閉:
func main() {
wait := &sync.WaitGroup{}
N := 3
CLOSE := make(chan struct{})
// Launch the goroutines
wait.Add(N)
for i := 1; i <= N; i++ {
go goFunc(wait, i, true /*** EVERY GOROUTINE WILL CLOSE ***/, CLOSE)
}
// Wait for the goroutines to finish
wait.Wait()
fmt.Println(`Exiting main`)
}
更改 goroutine 以便在關閉之前需要一段時間。我們希望兩個 goroutine 同時關閉:
// code for the actual goroutine
func goFunc(wait *sync.WaitGroup, i int, closer bool, CLOSE chan struct{}) {
defer wait.Done()
defer fmt.Println(`Exiting `, i)
T := time.Tick(time.Duration(100*i) * time.Millisecond)
for {
select {
case <-CLOSE:
return
case <-T:
fmt.Println(`Tick `, i)
if closer {
/*** TAKE A WHILE BEFORE CLOSING ***/
time.Sleep(time.Second)
close(CLOSE)
}
}
}
}
(https://play.golang.org/p/YHnbDpnJCks)
我們崩潰:
Tick 1
Tick 2
Tick 3
Exiting 1
Exiting 2
panic: close of closed channel
goroutine 7 [running]:
main.goFunc(0x40e020, 0x2, 0x68601, 0x430080)
/tmp/sandbox558886627/prog.go:24 +0x2e0
created by main.main
/tmp/sandbox558886627/prog.go:38 +0xc0
Program exited: status 2.
雖然關閉通道上的接收立即返回,但您無法關閉關閉的通道。
我們需要一點協調。我們可以用 async.Mutex和 abool來表示我們是否關閉了通道。讓我們創建一個結構來執行此操作:
type Close struct {
C chan struct{}
l sync.Mutex
closed bool
}
func NewClose() *Close {
return &Close {
C: make(chan struct{}),
}
}
func (c *Close) Close() {
c.l.Lock()
if (!c.closed) {
c.closed=true
close(c.C)
}
c.l.Unlock()
}
重寫我們的 gofunc 和我們的 main 以使用我們新的 Close 結構,我們很高興: https: //play.golang.org/p/eH3djHu8EXW
并發的問題在于,您總是需要想知道如果另一個“線程”在代碼中的其他任何地方會發生什么。
- 3 回答
- 0 關注
- 188 瀏覽
添加回答
舉報