В Go, имеет ли смысл писать неблокирующий код?
исходя из node.js точки зрения, где весь код не блокируется.
В Go неблокирование легко достигается с помощью каналов.
Если вы пишете сервер типа node.js в go, имеет ли смысл сделать его неблокирующим? например, при условии, что функция connect() базы данных возвращает канал, по сравнению с блокировкой в ожидании соединения.
для меня это кажется правильным подходом
но...
Ответы
Ответ 1
Блокирование и неблокирование не связаны с производительностью, они касаются интерфейса.
Если у вас есть один поток выполнения, тогда блокирующий вызов мешает вашей программе выполнять какую-либо полезную работу во время ожидания.
Но если у вас несколько потоков выполнения, блокирующий вызов не имеет особого значения, потому что вы можете просто оставить этот поток заблокированным и сделать полезную работу в другой.
В Go, goroutine заменяется на другой, когда он блокирует операции ввода-вывода. Во время выполнения Go использует неблокирующие системные вызовы ввода-вывода, чтобы избежать блокировки потока операционной системы, чтобы на нем мог запускаться другой goroutine, в то время как первый ожидает его ввода/вывода.
Goroutines действительно дешевы, поэтому писать неблокирующий код стиля не требуется.
Ответ 2
Записать функции блокировки. Язык позволяет легко превратить синхронный вызов в асинхронный.
Если вы хотите вызывать функцию асинхронно, используйте оператор go. Что-то вроде этого:
c := make(chan bool)
go func() {
blockingFunction()
c <- true
}()
// do some other stuff here while the blocking function runs
// wait for the blocking function to finish if it hasn't already
<-c
Ответ 3
В Go системные вызовы реализуются неблокирующим способом, используя наиболее эффективный базовый механизм, поддерживаемый ОС (например, epoll
). Если у вас нет другого кода для запуска, пока вы ждете результата вызова, он блокирует поток (из-за отсутствия лучшего результата), но если у вас есть альтернативные goroutines, они будут выполняться вместо этого.
Обратные вызовы (как вы привыкли использовать в js) допускают, по существу, одну и ту же основную механику, но, возможно, более умную гимнастику, необходимую для программиста.
В Go, ваш код для запуска после вызова функции задается сразу после вызова функции, а не определяется как обратный вызов. Код, который вы хотите запустить параллельно пути выполнения, должен быть завернут в goroutine, с сообщением через каналы.
Ответ 4
Для типичных приложений типа веб-сервера я бы рекомендовал не делать все асинхронным. Есть несколько причин.
-
Легче рассуждать о серийном блоке кода, чем асинхронный код (проще видеть ошибки)
-
Обработка ошибок golang основана на defer(), panic() и recover(), что, вероятно, не даст вам то, что вы хотите, с асинхронным кодом на 100%
-
Goroutines может протекать, если вы не будете осторожны [одно обсуждение]. Чем больше у вас асинхронного поведения, тем сложнее выявить эти проблемы и тем более вероятно, что они появятся.
Одна стратегия - сосредоточить асинхронность на высоком уровне и оставить все остальное блокирующим. Таким образом, у вас может быть "блок-обработчик", который логически отличен от "blob-обработчика запроса". Они оба работают в отдельных goroutines и общаются с использованием каналов. Но внутри "обработчика базы данных" блокируются блокировки подключения к базе данных и выполнения каждого запроса.
Вам не нужно выбирать асинхронный асинхронный или 0% асинхронный.
Ответ 5
Блокирующие интерфейсы всегда проще и лучше, чем неблокирующие. Красота Go заключается в том, что она позволяет вам писать параллельный (и параллельный) код в простой и простой причине, блокируя стиль.
Мода для неблокирующего программирования объясняется недостатками в языках, которые люди используют (особенно JavaScript), а не потому, что неблокирующее программирование по своей сути лучше.