網頁

2022/11/22

Golang 離開goroutine infinit loop by cancel context

Go經常利用goroutine中的infinit loop(無窮迴圈)去接收channel送來的訊息,而透過Context離開goroutine無窮迴圈的方式如下。


呼叫context.WithCancel取得cancel CancelFunc,並在滿足離開條件時呼叫cancel()

在goroutine無窮迴圈中使用select敘述選擇接收不同channel的處理。設定一case為當ctx.Done()因context的cancel()被呼叫而回傳關閉的channel時去調用return離開無窮迴圈。default case則執行離開迴圈前的一般邏輯。

至於exit channel是用來阻塞main程序的goroutine,直到另一條執行無窮迴圈的goroutine離開前exit<true傳入值才會解除<-exit的阻塞繼續往下執行。

main.go

package main

import (
    "context"
    "fmt"
    "time"
)

func main() {
    fmt.Println("start")
    ctx, cancel := context.WithCancel(context.Background()) // create cancel context

    ch := make(chan int) // make an unbuffered 'ch' channel
    go send(ch)          // send values to 'ch' channel

    exit := make(chan bool) // make an 'exit' channel. channel block begin
    go func(ctx context.Context) {
        for { // infinit loop
            select {
            case <-ctx.Done(): // when context's Done channel is closed by cancel()
                fmt.Println("done")
                exit <- true // send a value to 'exit' channel
                return
            default:
                i := <-ch // receive values from 'ch' channel
                fmt.Println(i)
                if i == 3 {
                    cancel() // close context's Done channel
                }
            }
        }
    }(ctx) // pass cancel context as a goroutine func's parameter
    <-exit // channel block end, receive value from 'exit' channel

    close(exit) // close channel
    fmt.Println("end")
}

func send(ch chan int) {
    for i := 1; i < 5; i++ {
        ch <- i
        time.Sleep(1 * time.Second)
    }
}

github


執行程式印出以下:

start
1
2
3
done
end


沒有留言:

張貼留言