亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Golang 模式一次殺死多個 goroutine

Golang 模式一次殺死多個 goroutine

Go
守候你守候我 2022-05-23 17:26:30
我有兩個 goroutine,如下面的代碼片段所示。我想同步它們,這樣當一個返回時,另一個也應該退出。實現這一目標的最佳方法是什么?func main() {  go func() {    ...    if err != nil {      return    }  }()  go func() {    ...    if err != nil {      return    }  }()}我在這里模擬了這個場景https://play.golang.org/p/IqawStXt7rt并試圖用一個通道來解決它,以表示一個例程已經完成。這看起來可能會寫入已關閉的通道,從而導致恐慌。解決此問題的最佳方法是什么?
查看完整描述

3 回答

?
慕神8447489

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()

}


查看完整回答
反對 回復 2022-05-23
?
慕田峪4524236

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")


查看完整回答
反對 回復 2022-05-23
?
喵喵時光機

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


并發的問題在于,您總是需要想知道如果另一個“線程”在代碼中的其他任何地方會發生什么。


查看完整回答
反對 回復 2022-05-23
  • 3 回答
  • 0 關注
  • 188 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號