簡體   English   中英

通過通道向多個go例程發送值

[英]Send value through channel to multiple go routines

我想在通道中發送一個值以從主函數執行例程。 發生的是哪個go例程將首先從通道接收值。

package main

import (
    "fmt"
    "math/rand"
    //"runtime"
    "strconv"
    "time"
)

func main() {
    var ch chan int
    ch = make(chan int)
    ch <- 1
    receive(ch)
}

func receive(ch chan int){
    for i := 0; i < 4; i++ {
        // Create some threads
        go func(i int) {
            time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
            fmt.Println(<-ch)
        }(i)
    }
}

我當前的實現給出了一個錯誤。

致命錯誤:所有goroutine都在睡着-死鎖!

我怎么知道哪個go例程將首先從通道接收值。 以及其他go例程會發生什么,如果這些例程將運行或引發錯誤,因為沒有通道可以接收該值。 因為其中之一已經收到了。

如果創建一個緩沖通道,我的代碼將起作用。 因此,我不了解發生在幕后的情況,這在創建如下所示的緩沖通道時使其起作用:

func main() {
    var ch chan int
    ch = make(chan int, 10)
    ch <- 1
    receive(ch)
}

如果我們看下面的代碼。 我可以看到我們可以直接通過通道發送值,而無需創建go例程以將某個通道的值發送到另一個go例程。

package main

import "fmt"

func main() {

    // We'll iterate over 2 values in the `queue` channel.
    queue := make(chan string, 2)
    queue <- "one"
    queue <- "two"
    close(queue)

    for elem := range queue {
        fmt.Println(elem)
    }
}

那我的代碼出了什么問題。 為什么會造成僵局。

未緩沖的通道(無長度)將阻塞,直到接收到該值為止。 這意味着寫入通道的程序將在寫入通道后停止,直到被讀取為止。 如果這在主線程中發生,則在您調用receive之前,它將導致死鎖。

還有兩個問題:您需要使用WaitGroup來暫停完成直到完成,並且通道的行為類似於並發隊列。 特別是,它具有推和彈出操作,均使用<-執行。 例如:

//Push to channel, channel contains 1 unless other things were there
c <- 1
//Pop from channel, channel is empty
x := <-c

這是一個工作示例:

package main

import (
        "fmt"
        "math/rand"
        "sync"
        "time"
)

func main() {
        var ch chan int 
        ch = make(chan int)
        go func() {
                ch <- 1
                ch <- 1
                ch <- 1
                ch <- 1
        }() 
        receive(ch)
}

func receive(ch chan int) {
        wg := &sync.WaitGroup{}
        for i := 0; i < 4; i++ {
                // Create some threads
                wg.Add(1)
                go func(i int) {
                        time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
                        fmt.Println(<-ch)
                        wg.Done()
                }(i)
        }   
        wg.Wait()
        fmt.Println("done waiting")
}

游樂場鏈接

如您所見, WaitGroup也非常簡單。 您可以在更高的范圍內聲明它。 它本質上是一個花哨的計數器,具有三種主要方法。 當您調用wg.Add(1) ,計數器增加,當您調用wg.Done() ,計數器減少,而當您調用wg.Wait() ,執行暫停,直到計數器達到0。

如果您所需要做的只是啟動多個工作程序並將任務發送給他們中的任何一個,那么您最好將值發送到通道之前先運行工作程序,因為正如@mkopriva所述,寫入通道是一項阻塞操作。 您始終必須擁有使用者,否則執行將凍結。

func main() {
    var ch chan int
    ch = make(chan int)

    receive(ch)

    ch <- 1
}

func receive(ch chan int) {
    for i := 0; i < 4; i++ {
        // Create some threads
        go func(i int) {
            time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
            fmt.Printf("Worker no %d is processing the value %d\n", i, <-ch)
        }(i)
    }
}

該問題的簡短答案“哪個例行程序會收到?” - 隨便 :)他們中的任何一個,你都不能肯定地說。

但是我不知道什么是時間。在那兒睡覺(...),保持原樣。

暫無
暫無

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

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