2017-03-28 95 views
0
package main 
import (
    "time" 
    "runtime" 
) 

var c = make(chan int, 2) 

func main() { 
    go worker(1) 
    for i := 0; i < 30; i++ { 
    go func() { 
      // k := i make a local copy not make any difference 
      c <- i 
    }() 
    } 
    time.Sleep(100* time.Second) 
} 

func worker(id int) { 
    for { 
    a := <-c 
    println(a, runtime.NumGoroutine()) 
    time.Sleep(time.Second) 
    } 
} 

輸出是不可預知的,有時像下面那樣。golang緩衝區通道意外結果

7 9 
13 29 
13 28 
13 27 
13 26 
13 25 
13 24 
13 23 
16 22 
16 21 
17 20 
19 19 
21 18 
21 17 
23 16 
25 15 
26 14 
26 13 
26 12 
26 11 
26 10 
26 9 
26 8 
26 7 
27 6 
27 5 
13 4 
28 3 
30 2 
30 2 

我知道如果緩衝區通道已滿,發件人會阻塞,並且當通道可用時發件人可以繼續。

  1. 爲什麼輸出不是恆定輸出0-29?如何使它?
  2. 變量/局部變量如何在goroutine中存儲?
  3. 如果很多發送者被阻塞,他們是否被FIFO命令喚醒?
+3

1:因爲goroutine執行_concurrently_。不要使用goroutines。 2.有關您的問題,請參閱https://golang.org/doc/faq#closures_and_goroutines。不,完全沒有。 – Volker

回答

2

輸出不是恆定的,因爲不同的goroutines共享相同的局部變量i。如果在goruoutine調用之前取消註釋並移動它,則會看到0-29的恆定輸出。更好的方法是將i變量移動到goroutine函數參數。

在規格中未指定喚醒訂單。你應該認爲它是一個隨機的。

0

3是FIFO

1由於內部產生的夠程for循環並不一定按順序執行。底層的Go調度器將隨機啓動一個(這是頻道如何調度它們的值)。當然,他們都會被創建,但他們會(在計劃中)從time.Sleep(...)開始調用main(調度程序是一個合作的調度程序,並且在函數調用,通道操作等某些點上這樣做 - 對於例如this

2使用信道直接:

var (
    c = make(chan int, 2) 
    wg = &sync.WaitGroup{} 
) 

func main() { 
    wg.Add(1) 
    go worker(1) 

    wg.Add(1) 
    go func() { 
     defer wg.Done() 
     for i := 0; i < 30; i++ { 
      c <- i 
     } 
     close(c) 
    }() 
    wg.Wait() 
} 

func worker(id int) { 
    defer wg.Done() 
    for a := range c { 
     println(a, runtime.NumGoroutine()) 
     time.Sleep(time.Second) 
    } 
} 

關於傳遞for循環變量;你幾乎正確地做了。您只需要將該行創建局域關閉,即在goroutine之外:

var (
    wg = &sync.WaitGroup{} 
) 

func main() { 
    for i := 0; i < 3; i++ { 
     localClosure := i // <- this line 

     wg.Add(1) 
     go func() { 
      defer wg.Done() 
      println(localClosure) 
     }() 
    } 

    wg.Wait() 
}