簡體   English   中英

如果sync.WaitGroup 類型的Wait() 方法阻塞,因此不是異步的,為什么要使用它?

[英]If the Wait() method of the sync.WaitGroup type blocks, and is thus not asynchronous, why use it?

我一直在研究 Golang,並通過其創新的 goroutines 構造了解它的並發性以及它對 coroutine-channel-only 模型的實施有多好。

我立即發現令人不安的一件事是使用Wait()方法,用於等待在父 goroutine 中產生的多個未完成的 goroutine 完成。 引用Golang 文檔

Wait 可以用來阻塞直到所有的 goroutines 完成

許多 Go 開發人員Wait()作為實現並發的首選方式這一事實似乎與 Golang 使開發人員能夠編寫高效軟件的使命背道而馳,因為阻塞是低效的,而真正的異步代碼永遠不會阻塞。

被阻塞的進程 [或線程] 是等待某個事件的進程,例如資源變得可用或 I/O 操作的完成。

換句話說,被阻塞的線程將花費 CPU 周期做任何有用的事情,只是反復檢查它當前正在運行的任務是否可以停止等待並繼續執行。

真正的異步代碼中,當協程遇到在結果到達之前無法繼續的情況時,它必須將其執行交給調度程序而不是阻塞,通過將其狀態從running切換到waiting ,以便調度程序可以開始執行下一個 -來自可運行隊列的內聯協程。 等待的協程應該只有在它需要的結果到達時才將其狀態從等待更改為可運行。

因此,由於Wait()阻塞直到 x 個 goroutine 調用了Done() ,調用Wait()的 goroutine 將始終處於可運行或正在運行狀態,浪費 CPU 周期並依賴調度程序搶占長時間運行的goroutine 只是將其狀態從 running 更改為 runnable,而不是將其更改為應有的等待。

如果這一切都是真的,並且我理解Wait()是如何正確工作的,那么為什么人們不使用內置的 Go 通道來完成等待子 goroutine 完成的任務呢? 如果我理解正確,發送到緩沖通道和從任何通道讀取都是異步操作,這意味着調用它們會使 goroutine 進入等待狀態,那么為什么它們不是首選方法呢?

我參考的文章舉了幾個例子。 這就是作者所說的“老派”方式:

package main

import (
    "fmt"
    "time"
)

func main() {
    messages := make(chan int)
    go func() {
        time.Sleep(time.Second * 3)
        messages <- 1
    }()
    go func() {
        time.Sleep(time.Second * 2)
        messages <- 2
    }()
    go func() {
        time.Sleep(time.Second * 1)
        messages <- 3
    }()
    for i := 0; i < 3; i++ {
        fmt.Println(<-messages)
    }
}

這是首選的“規范”方式:

package main

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

func main() {
    messages := make(chan int)
    var wg sync.WaitGroup
    wg.Add(3)
    go func() {
        defer wg.Done()
        time.Sleep(time.Second * 3)
        messages <- 1
    }()
    go func() {
        defer wg.Done()
        time.Sleep(time.Second * 2)
        messages <- 2
    }() 
    go func() {
        defer wg.Done()
        time.Sleep(time.Second * 1)
        messages <- 3
    }()
    wg.Wait()
    for i := range messages {
        fmt.Println(i)
    }
}

我可以理解第二個可能比第一個更容易理解,但第一個是異步的,沒有協程阻塞,第二個有一個協程阻塞:運行主函數的協程。 Wait()被普遍接受的另一個例子。

如果Wait()創建了一個低效的阻塞線程,為什么不被 Go 社區視為反模式? 為什么在這種情況下大多數人不首選通道,因為它們可以用來保持所有代碼異步和線程優化?

你對“阻塞”的理解是錯誤的。 阻塞操作如WaitGroup.Wait()或通道接收(當沒有值接收時)只會阻塞 goroutine 的執行,它們不會(必然)阻塞用於執行( ) 協程。

每當遇到阻塞操作(例如上面提到的)時,goroutine 調度程序可能(並且會)切換到另一個可能繼續運行的 goroutine。 WaitGroup.Wait()調用期間沒有(重要的)CPU 周期丟失,如果有其他 goroutines 可能會繼續運行,它們會繼續運行。

請檢查相關問題: Go 運行時使用的線程數

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM