Примітка:
Станом на Go 1.5, GOMAXPROCS встановлюється на кількість ядер апаратного забезпечення: golang.org/doc/go1.5#runtime , нижче вихідної відповіді до 1.5.
Коли ви запускаєте програму Go без зазначення змінної середовища GOMAXPROCS, програми Go планують виконуватися в одному потоці ОС. Однак, щоб програма здавалася багатопотоковою (саме для цього призначені програми, правда?), Планувальник Go іноді повинен перемикати контекст виконання, щоб кожна програма могла виконати свою роботу.
Як я вже говорив, коли змінна GOMAXPROCS не вказана, в середовищі виконання Go дозволяється використовувати лише один потік, тому неможливо перемикати контексти виконання, поки goroutine виконує якусь звичайну роботу, наприклад обчислення або навіть IO (що відображається на звичайні функції C). ). Контекст можна переключити лише тоді, коли використовуються примітиви одночасності Go, наприклад, коли ви вмикаєте кілька чанів, або (це ваш випадок), коли ви явно вказуєте планувальнику перемикати контексти - це те runtime.Gosched
, для чого.
Отже, коротко, коли контекст виконання в одній програмі досягає Gosched
виклику, планувальнику доручається переключити виконання на іншу програму. У вашому випадку є дві програми, основна (яка представляє "основний" потік програми) і додаткова, та, яку ви створили go say
. Якщо ви видалите Gosched
виклик, контекст виконання ніколи не перенесеться з першої програми на другу, отже, для вас не буде "світу". Коли Gosched
він присутній, планувальник передає виконання на кожній ітерації циклу з першої програми на другу і навпаки, тому у вас є чергування "привіт" та "світ".
FYI, це називається "багатозадачністю в кооперативі": програми регулярно передають контроль іншим програмам. Підхід, що застосовується в більшості сучасних ОС, називається "переважною багатозадачністю": потоки виконання не стосуються передачі управління; натомість планувальник прозоро перемикає контексти виконання на них. Кооперативний підхід часто використовується для реалізації `` зелених потоків '', тобто логічних паралельних підпрограм, які не пов'язують 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
hello
world
hello
world
world
...
Це може статися, якщо програми планують розділяти потоки ОС. Фактично так працює попереджувальна багатозадачність (або паралельна обробка у випадку багатоядерних систем): потоки паралельні, а їх комбінований вихід є невизначеним. До речі, ви можете залишити або видалити 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 середовище виконання Go змушує goroutines поступатися не тільки використанням примітивів паралельності, але і системних викликів ОС. Це означає, що контекст виконання може перемикатися між програмами також під час викликів функцій вводу-виводу. Отже, в останніх компіляторах Go можна спостерігати недетерміновану поведінку, навіть коли GOMAXPROCS не встановлено або встановлено на 1.