Что конкретно делает во время выполнения.Гощед делать?



на версия до выхода go 1.5 из тура Go website есть кусок кода, который выглядит так.



package main

import (
"fmt"
"runtime"
)

func say(s string) {
for i := 0; i < 5; i++ {
runtime.Gosched()
fmt.Println(s)
}
}

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


вывод выглядит так:



hello
world
hello
world
hello
world
hello
world
hello


что меня беспокоит, когда runtime.Gosched() удаляется, программа больше не печатает "мир".



hello
hello
hello
hello
hello


почему это так? Как это runtime.Gosched() влияет на исполнение?

668   2  

2 ответов:

когда вы запускаете программу Go без указания переменной среды GOMAXPROCS, GO goroutines запланированы для выполнения в одном потоке ОС. Однако, чтобы программа казалась многопоточной (это то, для чего предназначены goroutines, не так ли?), планировщик Go должен иногда переключать контекст выполнения, чтобы каждый goroutine мог выполнять свою часть работы.

как я уже сказал, когда переменная GOMAXPROCS не указана, Go runtime разрешено использовать только один поток, поэтому это невозможно чтобы переключать контексты выполнения, в то время как goroutine выполняет некоторую обычную работу, такую как вычисления или даже IO (который сопоставляется с простыми функциями C). Контекст может быть переключен только тогда, когда используются примитивы параллелизма Go, например, когда вы включаете несколько chans или (это ваш случай), когда вы явно говорите планировщику переключать контексты - это то, что runtime.Gosched - это для.

короче говоря, когда контекст выполнения в одном goroutine достигает Gosched звонок, планировщик приказано переключить исполнение на другую горутину. В вашем случае есть два goroutines, основной (который представляет собой "основной" поток программы) и дополнительный, тот, который вы создали с go say. Если вы удалите Gosched вызов, контекст выполнения никогда не будет перенесен из первого goroutine во второй, следовательно, нет " мир " для вас. Когда Gosched присутствует, планировщик передает выполнение на каждой итерации цикла от первого goroutine ко второму и наоборот, так что у вас есть "привет"и" мир " чередуются.

FYI, это называется "кооперативная многозадачность": goroutines должны явно передавать управление другим goroutines. Подход, используемый в большинстве современных ОС, называется "упреждающей многозадачностью": потоки выполнения не связаны с передачей управления; планировщик прозрачно переключает контексты выполнения на них. Кооперативный подход часто используется для реализации "зеленых потоков", то есть логических параллельных сопрограмм, которые не сопоставляйте 1: 1 с потоками ОС - так реализуется GO runtime и его goroutines.

обновление

я упомянул переменную среды GOMAXPROCS, но не объяснил, что это такое. Пора это исправить.

когда эта переменная установлена в положительное число N, Go runtime сможет создавать до N собственные потоки, на которых будут запланированы все зеленые потоки. Родной поток вид потока, который создается операционная система (Windows threads, pthreads и т. д.). Это означает, что если N больше 1, возможно, что goroutines будут планироваться для выполнения в разных собственных потоках и, следовательно, работать параллельно (по крайней мере, до возможностей вашего компьютера: Если ваша система основана на многоядерном процессоре, вполне вероятно, что эти потоки будут действительно параллельными; если ваш процессор имеет одно ядро, то упреждающая многозадачность, реализованная в потоках ОС, создаст видимость параллельной работы исполнение.)

можно установить переменную GOMAXPROCS с помощью runtime.GOMAXPROCS() функция вместо предварительной установки переменной среды. Используйте что-то вроде этого в своей программе вместо текущего main:

func main() {
    runtime.GOMAXPROCS(2)
    go say("world")
    say("hello")
}

в этом случае вы можете наблюдать интересные результаты. Возможно, что вы получите строки "hello" и "world", напечатанные неравномерно, например

hello
hello
world
hello
world
world
...

это может произойти, если goroutines планируется разделить потоки ОС. Это на самом деле, как работает упреждающая многозадачность (или параллельная обработка в случае многоядерных систем): потоки параллельны, а их объединенный выход является неопределенным. Кстати, вы можете оставить или удалить Gosched вызов, кажется, не имеет никакого эффекта, когда GOMAXPROCS больше 1.

вот что я получил на нескольких запусках программы с runtime.GOMAXPROCS звонок.

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 compiler Go runtime заставляет goroutines уступать не только использованию примитивов параллелизма, но и системным вызовам ОС. Это означает, что контекст выполнения может переключаться между goroutines также при вызовах функций ввода-вывода. Следовательно, в последних компиляторах Go можно наблюдать индетерминированное поведение даже тогда, когда GOMAXPROCS не установлен или установлен в 1.

совместное планирование является виновником. Не уступая, другой (скажем, "мир") goroutine может юридически получить нулевые шансы на выполнение до/когда main завершается, что в соответствии с спецификациями завершает все gorutines - ie. весь процесс.

Comments

    Ничего не найдено.