Почему требуется время для выполнения определенных goroutines?
В уроке 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")
}
Запуск этого кода дает ожидаемые результаты ("мир" и "привет" записываются на экран взаимозаменяемо 5 раз).
Однако, если мы закомментируем time.Sleep
(и, следовательно, строку "time"
импорта) и снова запустим программу, у нас останется only "привет", записанная на экран пять раз.
Что такого важного в time.Sleep
, которое спасает горутин от смерти?
Ответы
Ответ 1
Если вы удалите time.Sleep
, вы не дадите gotoutine say("world")
шанс на запуск. Планировщик goroutine не является превентивным. Ваши goroutines должны отказаться от контроля до того, как другой goroutine будет работать. Один из способов отказаться от управления - запустить time.Sleep
.
Если вы вытащите time.Sleep
из функции say
, тогда первичный goroutine запускается 5 раз, не отказываясь от управления вторичной goroutine, а затем, когда первичный goroutine возвращается из say
, программа выходит из-за того, что ничего нет для сохранения программы.
Ответ 2
Поскольку планировщик процедур не имеет преимуществ, ваши программы должны отказаться от контроля, прежде чем будет запущена другая программа. Один из способов отказаться от управления с помощью time.Sleep
. Другой способ - с runtime.Gosched()
.
Вот учебник, модифицированный для использования Gosched(): http://play.golang.org/p/jQ9mlGYXXE
Это полезный урок в понимании горутин. Однако попытка напрямую управлять планировщиком - это определенно анти-паттерн; часто последует горе.
Вместо этого, подумайте больше о рутинных программах, таких как блоки обмена цифровым оборудованием (конечные автоматы - хорошая аналогия). Лучше узнать о модели последовательных процессов, на которой основаны процедуры. В схеме на основе CSP каждая goroutine имеет свое собственное частное состояние и обменивается сообщениями для взаимодействия с состоянием других goroutines. Передача сообщений вызывает синхронизацию, которую планировщик использует для определения того, какое действие получает процессорное время, а какое - в очередь ожидания.
Когда вы подходите к Go таким образом, вам, вероятно, никогда не придется беспокоиться о внутренностях планировщика.
Ответ 3
Если вы удаляете time.Sleep из функции say, main выполнит say ( "hello" ) и завершится без выполнения goroutine. Если вы добавите time.Sleep(или иначе выберите {}) до основного конца, он даст время для запуска goroutine и этот поток будет выбран из планировщика.
Пример:
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")
time.Sleep(1*time.Second)
// Vs:
// select {} // blocks indefinitely, requires manual interrupt
// In CSP-speak the empty select is like STOP.
// for{} would cause the cpu to max and the process STATE will be `running`
// select{} will not cause the cpu to max and the process state will be `sleeping`
}
Вывод обычно будет 5 привет, а затем 5 мира, но он также может распечатать один из миров до последнего приветствия
ПОПРОБУЙТЕ ИТ → (http://) goo.gl/K2v7H0