Когда горутина вызывает функцию C с использованием Cgo, среда выполнения Go должна переключиться с выполнения кода Go на M, управляемом планировщиком Go, на выполнение кода C в потенциально другом потоке ОС, управляемом средой выполнения C. Этот переход имеет последствия:
-
M, выполняющий вызов Cgo, по сути, посвящен этому коду C до тех пор, пока он не вернется. В это время он не может запускать другие горутины.
-
Если код C блокирует, то M будет блокироваться. Планировщик Go не может вытеснить горутину, выполняющую код C.
-
Планировщик Go отсоединит P от M, заблокированного в вызове Cgo, аналогично блокировке системных вызовов, позволяя другим горутинам работать на другой паре M/P.
-
Потенциальные последствия для производительности:
-
Накладные расходы: Переход между контекстами выполнения Go и C имеет накладные расходы. Частые небольшие вызовы Cgo могут быть дорогостоящими.
-
Блокировка: Если код C выполняет операции блокировки, не освобождая поток ОС, он может связать M и потенциально ограничить количество горутин, которые могут выполняться одновременно, если вы ограничили M.
-
Планирование задержек. Планировщик Go имеет меньшую видимость того, что делает код C, что иногда может привести к менее оптимальным решениям по планированию.
-
Сложность. Отладка и профилирование кода, использующего Cgo и параллелизм, может оказаться более сложной задачей.
Итог
Когда горутина вызывает функцию C с использованием Cgo, среда выполнения Go должна переключиться с выполнения кода Go на M, управляемом планировщиком Go, на выполнение кода C в потенциально другом потоке ОС, управляемом средой выполнения C.