首頁  >  文章  >  後端開發  >  Go 程式在 goroutine 工作完成之前退出

Go 程式在 goroutine 工作完成之前退出

PHPz
PHPz轉載
2024-02-08 22:57:20883瀏覽

Go 程序在 goroutine 工作完成之前退出

php小編小新在這篇文章中將介紹一個關於Go程式的重要問題:在goroutine工作完成之前就退出的情況。在Go語言中,goroutine是一種輕量級的線程,可以並發執行任務。然而,當我們的程式可能在goroutine工作完成之前退出時,我們需要了解如何處理這種情況,以確保我們的程式能夠正確地完成任務。在接下來的內容中,我們將探討這個問題,並提供一些解決方案來解決它。

問題內容

我在了解如何正確封鎖和關閉頻道時遇到問題。我正在啟動任意數量的工作人員,我發現我的主要功能要么在工作人員完成之前退出,要么由於未關閉的通道而掛起。我需要一種更好的方法來阻止工作人員在不退出主通道的情況下讀取通道,然後在完成後優雅地關閉通道以結束循環。我所做的任何嘗試都以僵局告終。

我嘗試了一些方法,包括使用等待群組,但問題仍然存在。我注意到,透過新增 time.sleep,程式按預期工作,但將其註解掉會導致沒有完成任何工作。

time.sleep(time.duration(10 * time.second))

這是一個可運行的範例 https://go.dev/play/p/qhqnj-ajqbi,其中保留了 sleep。這是註解掉睡眠超時的損壞代碼。

package main

import (
    "fmt"
    "sync"
    "time"
)

// some complicated work
func do(num int, ch chan<- int) {
    time.sleep(time.duration(500 * time.millisecond))
    ch <- num
}

func main() {

    results := make(chan int)

    // for some number of required complicated work
    for i := 0; i < 53; i++ {
        go do(i, results)
    }

    var wg sync.waitgroup

    // start 3 workers which can process results
    for i := 0; i < 3; i++ {
        wg.add(1)
        go func(id int) {
            defer wg.done()
            worker(id, results)
        }(i)
    }

    // handle closing the channel when all workers complete
    go func() {
        wg.wait()
        close(results)
    }()

    //time.sleep(time.duration(10 * time.second))

    fmt.println("donezo")
}

// process the results of do() in a meaningful way
func worker(id int, ch <-chan int) {
    fmt.println("starting worker", id)

    for i := range ch {
        fmt.println("channel val:", i)
    }
}

我還嘗試將 defer wg.done() 移動到 worker() func 內部,但這是同樣的問題,並且在沒有睡眠的情況下無法工作。

// process the results of do() in a meaningful way
func worker(wg *sync.WaitGroup, id int, ch <-chan int) {
    fmt.Println("starting worker", id)

    defer wg.Done()

    for i := range ch {
        fmt.Println("channel val:", i)
    }
}

我是否選擇了錯誤的範式,或者我只是使用了錯誤的範式?

解決方法

我最初問「我可以對我的程式碼進行一些小調整來使其工作嗎?還是我必須重新考慮這個問題?」我發現的答案是,是的,有是一個小調整。

我必須學習一個關於通道的有趣的基本概念:您可以從封閉的通道中讀取數據,即排空通道。正如我最初的範例中提到的range 永遠不會終止,因為我找不到關閉通道的好地方,即使當我以其他創造性的方式強制它時,程式也會表現出不良行為

  • 未處理完頻道內的所有內容而退出
  • 死鎖或在封閉通道上發送

這是因為「真實」程式碼的細微差別,其中處理通道內容所需的時間比填充所需的時間更長頻道和事物不同步。

由於我的發送者中沒有明確的實用方法來關閉通道(99% 的通道教程中都建議這樣做),因此當您有多個工作人員正在讀取通道並且工作人員不知道時,透過goroutine 在main 中執行此操作實際上是可以接受的其中哪些讀取了最後一個值。

解決方案

我將工作人員包裝在自己的sync.waitgroup 中,並使用worker.wait()阻止程序退出,從而允許工作“完成” ”。當沒有更多數據要發送時,我獨立地close() 通道,即我通過使用他們自己的等待組等待編寫者完成來阻止。close為範圍循環提供了一個終止情況,因為當返回通道的預設值時,即到達通道結束時返回eof 類型,它將結束。阻塞交會通道沒有終點,直到它被關閉。

我對此的看法是,如果您不知道將並行推送多少個值,則 go 無法知道無緩衝通道的長度,因為它正在範圍內,直到您關閉它。 。由於關閉,它表示讀取剩餘的任何內容,直到終止值或結束。 workers.wait() 會阻塞,直到完成。

已解決操作的範例 https://www.php.cn/link/2bf0ccdbb4d3ebbcb990af74bd78c658

讀取關閉通道的範例 https://www.php.cn/link/d5397f1497b5cdaad7253fdc92db610b

輸出

filling 0
filling 1
filling 2
filling 3
filling 4
filling 5
filling 6
filling 7
filling 8
filling 9
closed
empyting 0
empyting 1
empyting 2
empyting 3
empyting 4
empyting 5
empyting 6
empyting 7
empyting 8
empyting 9

以上是Go 程式在 goroutine 工作完成之前退出的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除