что рантайм горутину отдаёт в тред и она там крутится. А оказывается, что одна часть горутины выполняется в одном треде ОС, а другая часть горутины в другом треде ОС.
Взять простой пример:
$ cat test.go
package main
import (
"net/http"
"fmt"
"time"
"strconv"
)
func main() {
time.Sleep(2 * time.Second)
fmt.Println("let's go!")
for i:=0; i<20; i++{
fname := "func"+strconv.Itoa(i)
go doJob(fname)
}
time.Sleep(7 * time.Second)
}
func doJob(fname string) {
fmt.Println(fname)
resp, err := http.Get("http://ya.ru/")
fmt.Println(resp, err)
time.Sleep(5 * time.Second)
}
Стрейсим (до сих пор не пойму где ставить 2>&1, лол):
2>&1 strace -f go run test.go > /tmp/strace.goha 2>&1
А потом анализируем.
Сискол write() вызывался только в двух ОС тредах: 16502 и 16504:
$ grep write /tmp/strace.goha | grep func | cut -d' ' -f 2 | sort | uniq -c | sort -nrk1
19 16502]
1 16504]
При этом в потоке 16502 сокет открывался только 9 раз, что не совпадает с числом вызовов write() - 19:
$ grep 'socket(' /tmp/strace.goha | cut -d' ' -f 2 | sort | uniq -c | sort -nrk1
25 16506]
22 16509]
14 16505]
11 16508]
10 16510]
9 16504]
9 16502]
Какая тут логика?
c go 1.14 планировщик у нас preemtible. Те неважно в какой стадии находится горутина - ее можно прервать и вызывать другую на том же треде Очень занимательный видос как это работает и как не получилось сделать https://www.youtube.com/watch?v=1I1WmeSjRSw
причина зачем это сделано - сборщик мусора. он вынужден останавливать все треды периодически.
> Те неважно в какой стадии находится горутина - ее можно прервать и вызывать другую на том же треде Ну это вроде норм. А вот зачем горутину переносить в другой тред ОС? Я думаю, что рантайм дробить горутину на части и какие-то части отдаёт одному треду, а какие-то другому. Например, сетевые сисколы в один тредик, файловые - в другой. Об это как-то говорил @onokonem , но деталей не было. Спасибо за видос. Посмотрю
кстати, он обязательно стопит все треды разом, типа стоп мир по всем фронтам или таки может почистить сначала один тредик ОС, потом другой?
Это не совсем так. Вытеснение используется только как fallback
> А вот зачем горутину переносить в другой тред ОС? потому что треды это воркер пул, который выполняет горутины. и порядок там сложно предсказать. как раз таки ассоциировать рутину с тредом было сложнее, имхо
стопит все разом но на 2 коротких этапа - доли микросекунд. между ними работает конкурретно и без блокировок.
если горутине надо поспать, например, в ожидании сетевого ввода-вывода, планировщик снимает ее с очереди на выполнение. когда соответствующее событие сетевое произойдет - планировщик отправляет горутину на один из тредов в пуле, чтобы она могла обработать то, что там произошло и, естественно, это совершенно не обязательно будет тот же тред.
в видосе что выше скинул, про вытеснение говорят как про основной метод остановки рутин для переключения. видос 20го года про го 1.14 - должно быть актуально. можете кинуть ссылок в уточняющий источник плз
именно отправляет или помечает, что горутину можно выполнить?
а в чем разница?
ну, сразу к треду привязать или возможно сразу выполним, а может потом
я видос смотреть сейчас не буду, поэтому и спорить с ним не стану.
там микс подходов с GC, но якобы OS signals основной и единственный способ остановки.
там есть глобальная и локальная очередь, и, если мне не изменяет память, шедулер после I/O помещает в локальную
кроме того, там есть work stealing
локальная это локальная для P (m-p-g) имеется в виду?
Обсуждают сегодня