Управляет ли планировщик Go также потоками, отличными от Go, созданными во время выполнения?

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

Таким образом, это в основном означает, что выполнение подпрограмм Go, включая основную горутину, управляется как планировщиком go, так и планированием потоков ОС.

Теперь вот мои вопросы..

  1. Будет ли выполнение горутины полностью управляться планированием потоков ОС, если я вызову runtime.LockOSThread() в начале этой горутины?

  2. Полностью ли выполнение потока, отличного от Go, также полностью управляется планированием потоков ОС? Другими словами, если я создаю поток, отличный от Go, с помощью функции CreateThread (Windows), то управление выполнением потока, отличного от Go, выходит за рамки планировщика времени выполнения Go?

  3. Что, если я запущу еще одну горутину с go func() в этом потоке, отличном от Go? Как управляется выполнение этого потока, не относящегося к Go, и горутины?

  4. В настоящее время я пишу программу на Golang, которая запускает цикл сообщений Windows в функции main() программы go. Большую часть времени это работало хорошо, но иногда цикл сообщений блокировался и возобновлялся через несколько секунд, а затем загружалось большое количество старых сообщений. (Еще один мой вопрос: Цикл сообщений Windows блокируется и возобновляется с перерывами (голанг))

    Я понятия не имел, почему это происходит, поэтому я подозревал, что основная горутина переключает поток ОС с помощью планировщика go. Поэтому я добавил runtime.LockOSThread() в начале функции main(), чтобы цикл сообщений Windows всегда выполнялся в одном и том же потоке. Однако проблема все же возникла!

    Я до сих пор не знаю, почему это происходит, но я подозреваю, что это из-за планировщика Go, потому что та же логика, написанная на Python 3.4, не создавала таких проблем.

    Итак, сейчас я пытаюсь создать новый поток Windows (не-Go Thread), вызвав функцию CreateThread(...) и запустив цикл сообщений Windows в этом потоке.

    Но мне любопытно, отличается ли этот подход от вызова runtime.LockOSThread() в основной горутине, выполняющей цикл сообщений Windows, с точки зрения планировщика времени выполнения Go.

    Итак, мой вопрос: «Если я создаю новый поток, отличный от Go, с функцией CreateThread(...) и запускаю цикл сообщений Windows в этом потоке, не влияет ли планировщик времени выполнения Go на выполнение этого потока?»

Любая помощь или идеи будут с благодарностью. Спасибо.


person asqdf    schedule 16.02.2017    source источник
comment
Я совершенно уверен, что вы не можете безопасно использовать новый поток ОС без поддержки из среды выполнения, либо позволяя среде выполнения создавать поток, либо используя cgo. Вы не можете прикрепить горутину к потоку лучше, чем с помощью LockOSThread. Я думаю, вам нужно выяснить, что делает ваше приложение, пока оно заблокировано.   -  person JimB    schedule 16.02.2017
comment
@JimB Спасибо за ваш комментарий. Мне трудно понять, что делает мое приложение, пока оно заблокировано... потому что эта проблема возникает в случайное время. Не могли бы вы дать мне какие-либо предложения или инструменты для выяснения того, что делает моя программа, пока она заблокирована? Спасибо   -  person asqdf    schedule 16.02.2017
comment
Проверьте документы пакета runtime и посмотрите, можете ли вы использовать одну из настроек переменной среды GODEBUG, чтобы увидеть, соответствуют ли получаемые вами паузы паузам сборщика мусора или нет. В качестве альтернативы вы можете попробовать это: используйте CreateThread() из некоторого кода C, чтобы создать поток, о котором Go не знает, и создайте там свое окно и перекачивайте сообщения. Управление памятью здесь будет немного сложнее, но SendMessageW() можно использовать для отправки сообщений между потоками, поэтому базовая связь от Go к C будет работать. (Я собирался сделать это с моим пакетным интерфейсом перед тем, как начать переписывать libui. Я все еще мог бы это сделать.)   -  person andlabs    schedule 16.02.2017
comment
Вместо того, чтобы продолжить работу над своим предыдущим вопросом и ответить, что runtime.LockOSThread() вам не помогло, вы публикуете новый вопрос с общими и неясными вопросами, а затем продолжаете обсуждение проблемы вашего предыдущего вопроса? Для этого и предыдущий пост! Что касается вашего вопроса об используемых инструментах: (1) прикрепите отладчик после блокировки; (2) Создайте дамп после блокировки; (3) Используйте Process Explorer/Process Hacker, чтобы увидеть, что делают потоки, когда вы заблокированы; (4) Запустите вашу программу под профилировщиком; (5) ...   -  person conio    schedule 17.02.2017


Ответы (1)


Если вы запустите новый поток ОС с помощью подпрограммы CreateThread(), планировщик Go не коснется этого потока. Однако затем вам нужно будет реализовать способ связи этого потока с горутинами. Например, вы не можете вызвать метод Go непосредственно из потока, созданного CreateThread(). Вместо этого вам придется использовать некоторую систему на основе C для опроса событий из горутины.

Кроме того, если вы хотите запустить цикл из основного потока ОС, вы должны вызывать LockOSThread() в init(), а не в main(). См. https://github.com/golang/go/wiki/LockOSThread:

func init() {
    runtime.LockOSThread();
}
func main() {
    // Run loop here.
}
person Alex Nichol    schedule 17.02.2017
comment
Спасибо за Ваш ответ. В чем разница вызова LockOSThread() в функции init() и функции main()? Разве вызов горутины не блокируется потоком ОС, если я вызываю LockOSThread() в начале функции main()? - person asqdf; 17.02.2017
comment
Использование LockOSThread() в init гарантирует, что main() запускается из основного потока ОС. Часто специфичные для ОС API, такие как циклы выполнения, работают правильно только в том случае, если они вызываются из основного потока. Я не знаю, так ли это в случае с циклом выполнения Windows - возможно, это не так. - person Alex Nichol; 17.02.2017
comment
Действительно поздно, но на благо гуглеров: пока это явно не задокументировано, runtime.LockOSThread() ничего не гарантирует, даже если работает в init(). Конечно, Windows не волнует, в каком потоке вы выполняете свои действия, связанные с потоком, до тех пор, пока вы выбираете один. (OS X является примером ОС, которая требует, чтобы циклы выполнения находились в первом потоке, созданном ОС.) - person andlabs; 29.05.2017