2016-04-17 3 views
1

Я играл со следующим кодом от A Tour of Go, но я не понимаю, что происходит, когда я применяю некоторые незначительные изменения. Исходный код этоПопытка понять goroutines

package main 

import (
    "fmt" 
    "time" 
) 

func say(s string) { 
    for i := 0; i < 5; i++ { 
     time.Sleep(100 * time.Millisecond) 
     fmt.Println(s) 
    } 
} 

func main() { 
    go say("world") 
    say("hello") 
} 

и производит этот

world 
hello 
hello 
world 
world 
hello 
hello 
world 
world 
hello 

, который OK: пять раз привет, пять раз в мире. Я начинаю получать странно, когда я называю

say("world") 
go say("hello") 

Теперь выход только

world 
world 
world 
world 
world 

Нет привет вообще. Он становится еще более странным с двумя гортанами

Теперь нет выхода. Когда я изменяю i < 5 в i < 2 и вызвать

go say("world") 
say("hello") 

Я получаю

world 
hello 
hello 

Что я здесь отсутствует?

+0

https://gobyexample.com/goroutines – elithrar

ответ

5

В случае

say("world") 
go say("hello") 

«Мир» вызов должен завершить до «привет» goroutine запускается. Голосование «привет» не запускается или не завершается because main returns.

Для

в goroutines не работают или полной, потому что основные прибыли.

Использование sync.WaitGroup для предотвращения основной от выхода до goroutines комплектных:

func say(wg *sync.WaitGroup, s string) { 
    defer wg.Done() 
    for i := 0; i < 5; i++ { 
    time.Sleep(100 * time.Millisecond) 
    fmt.Println(s) 
    } 
} 

func main() { 
    var wg sync.WaitGroup 
    wg.Add(2) 
    go say(&wg, "world") 
    go say(&wg, "hello") 
    wg.Wait() 
} 

playground example

1

Это потому, что основная функция была завершена.

При возврате основной функции все goroutines внезапно завершаются, а затем завершаются программы.

Вы добавляете Постулаты:

time.Sleep(100 * time.Second) 

перед основной функцией возврата, и все идет хорошо.

Но хорошей практикой в ​​Go является использование канала, который используется для связи между goroutines. Вы можете использовать его, чтобы главная функция ожидала завершения фоновых goroutines.

0

С

func main() { go say("world") say("hello") }

Вы создаете два отдельных goroutines, один основные функции горутин и один - говорят («мир»). Обычно, когда функции выполняются, программы переходят к этой функции, выполняют весь код внутри, а затем переходят к строке после вызова функции.

С goroutines вы не прыгаете внутри функции, но вы запускаете goroutine в отдельном потоке и продолжаете выполнять линию сразу после вызова, не дожидаясь его.

Поэтому у горутинца не будет времени, чтобы закончить до того, как закончится главный горутин.

2

Поздравляем с обучением Go. Как кто-то новый, приятно понимать параллелизм и то, как он отличается от параллелизма.

Параллелизм
Параллелизм как жонглер жонглирование несколько шаров в воздухе, с одной стороны. Независимо от того, сколько мячей он жонглирует, только один мяч касается его руки в любой момент.

параллелизм
Когда фокусник начинает жонглировать больше шаров с другой стороны, параллельно, мы имеем два параллельных процессы работает одновременно.

Goroutines является большим, потому что они оба одновременно и автоматические параллельно, в зависимости от вычислительных ядер, доступных и GOMAXPROCS переменного, значения которой устанавливается.

Один руки Жонглер
Назад к одной руки, одному порошковым, параллельному жонглеру. Представьте, что он жонглирует тремя шарами под названием «hello», «world» и «mars» соответственно, при этом рутина представляет собой рутину main.

var balls = []string{"hello", "world", "mars"} 

func main() { 
     go say(balls[0]) 
     go say(balls[1]) 
     go say(balls[2]) 
} 

Или более уместно,

func main() { 
     for _, ball := range balls { 
       go say(ball) 
     } 
} 

После того, как три шара выбрасываются в воздух последовательно, фокусник просто отступает его руку сразу. То есть, выход main заканчивается, прежде чем первый брошенный мяч может даже приземлиться на его руку. Позор, шары просто падают на землю. Плохое шоу.

Для того, чтобы вернуть шары в руке, жонглер должен убедиться, что он ждет для них. Это означает, что его рука должна быть в состоянии отслеживать и подсчитывать шары, которые он бросает, и учиться, когда они приземляются.

Самый простой способ заключается в использовании sync.WaitGroup:

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

var balls = []string{"hello", "world", "mars"} 
var wg sync.WaitGroup 

func main() { 
     for _, ball := range balls { 
       // One ball thrown 
       wg.Add(1) 
       go func(b string) { 
         // Signals the group that this routine is done. 
         defer wg.Done() 
         // each ball hovers for 1 second 
         time.Sleep(time.Duration(1) * time.Second) 
         fmt.Println(b) 
         // wg.Done() is called before goroutine exits 
       }(ball) 
     } 

     // The juggler can do whatever he pleases while the 
     // balls are hanging in the air. 

     // This hand will come back to grab the balls after 1s. 
     wg.Wait() 
} 

WaitGroup проста. Когда появляется goroutine, один добавляет «счетчик отставания» с WaitGroup.Add(1) и вызывает WaitGroup.Done(), чтобы уменьшить счетчик. Когда отставание становится 0, это означает, что все goroutines сделаны, и WaitGroup должен прекратить ждать (и захватить шары!).

При использовании каналов (каналов) для синхронизации в порядке, рекомендуется использовать доступные параллельные инструменты, особенно если использование каналов делает код более сложным и сложным для понимания.

Смежные вопросы