Заметка:
Начиная с Go 1.5, GOMAXPROCS настроен на количество ядер оборудования: golang.org/doc/go1.5#runtime , ниже исходного ответа до 1.5.
Когда вы запускаете программу Go без указания переменной среды GOMAXPROCS, горутины Go планируются для выполнения в одном потоке ОС. Однако, чтобы программа выглядела многопоточной (для чего нужны горутины, не так ли?), Планировщик Go должен иногда переключать контекст выполнения, чтобы каждая горутина могла выполнять свою часть работы.
Как я уже сказал, когда переменная GOMAXPROCS не указана, среде выполнения Go разрешено использовать только один поток, поэтому невозможно переключать контексты выполнения, пока goroutine выполняет некоторую обычную работу, например вычисления или даже ввод-вывод (который отображается на простые функции C. ). Контекст может переключаться только тогда, когда используются примитивы параллелизма Go, например, когда вы включаете несколько каналов, или (это ваш случай), когда вы явно указываете планировщику переключать контексты - это то runtime.Gosched
, для чего.
Короче говоря, когда контекст выполнения в одной горутине достигает Gosched
вызова, планировщик получает указание переключить выполнение на другую горутину. В вашем случае есть две горутины, основная (которая представляет «основной» поток программы) и дополнительная, которую вы создали go say
. Если вы удалите Gosched
вызов, контекст выполнения никогда не будет перенесен из первой горутины во вторую, поэтому для вас не будет «мира». Когда Gosched
присутствует, планировщик передает выполнение на каждой итерации цикла от первой горутины ко второй и наоборот, так что у вас чередуются «привет» и «мир».
К вашему сведению, это называется «кооперативная многозадачность»: горутины должны явно передавать управление другим горутинам. Подход, используемый в большинстве современных операционных систем, называется «вытесняющей многозадачностью»: потоки выполнения не связаны с передачей управления; планировщик прозрачно переключает им контексты выполнения. Кооперативный подход часто используется для реализации «зеленых потоков», то есть логических параллельных сопрограмм, которые не отображают 1: 1 в потоки ОС - так реализуется среда выполнения Go и ее горутины.
Обновить
Я упомянул переменную среды GOMAXPROCS, но не объяснил, что это такое. Пора это исправить.
Если для этой переменной установлено положительное число N
, среда выполнения Go сможет создавать до N
собственных потоков, для которых будут запланированы все зеленые потоки. Родной поток - это своего рода поток, который создается операционной системой (потоки Windows, pthreads и т. Д.). Это означает, что если N
больше 1, возможно, что горутины будут запланированы для выполнения в разных собственных потоках и, следовательно, будут выполняться параллельно (по крайней мере, с учетом возможностей вашего компьютера: если ваша система основана на многоядерном процессоре, она вполне вероятно, что эти потоки будут по-настоящему параллельными; если ваш процессор имеет одноядерный процессор, то вытесняющая многозадачность, реализованная в потоках ОС, создаст видимость параллельного выполнения).
Можно установить переменную GOMAXPROCS с помощью runtime.GOMAXPROCS()
функции вместо предварительной установки переменной среды. Используйте в своей программе что-то подобное вместо текущего main
:
func main() {
runtime.GOMAXPROCS(2)
go say("world")
say("hello")
}
В этом случае можно наблюдать интересные результаты. Возможно, что вы получите напечатанные строки hello и world с неравномерным чередованием, например
hello
hello
world
hello
world
world
...
Это может произойти, если горутины запланированы для разделения потоков ОС. Фактически, именно так работает вытесняющая многозадачность (или параллельная обработка в случае многоядерных систем): потоки параллельны, а их комбинированный вывод является неопределенным. Кстати, вы можете оставить или удалить Gosched
вызов, похоже, это не действует, когда GOMAXPROCS больше 1.
Вот что я получил при нескольких запусках программы с runtime.GOMAXPROCS
call.
hyperplex /tmp % go run test.go
hello
hello
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
hello
hello
hello
hello
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
Видите ли, иногда результат бывает красивым, иногда нет. Индетерминизм в действии :)
Еще одно обновление
Похоже, что в более новых версиях компилятора Go среда выполнения Go заставляет горутины уступать не только при использовании примитивов параллелизма, но и при системных вызовах ОС. Это означает, что контекст выполнения можно переключать между горутинами также при вызове функций ввода-вывода. Следовательно, в последних компиляторах Go можно наблюдать недетерминированное поведение, даже когда GOMAXPROCS не установлен или установлен в 1.