Таким образом, помимо обработки нескольких запросов сервера есть ли другое время, когда concurrency имеет значение? Я спрашиваю, потому что это так встроено в то, что я чувствую расточительность, если не использую его, но я едва могу использовать его для этого.
Когда следует использовать concurrency в golang?
Ответ 1
Вот хороший пример одного из изобретателей Go, Роб Пайка, использования concurrency, потому что это более простой способ выразить решение проблемы:
Обобщая это немного, любая проблема производителя-потребителя является естественным образом подходит для 2 goroutines, используя канал для передачи выходов от производителя к потребителю.
Другое хорошее использование для concurrency взаимодействует с несколькими источниками ввода/вывода (диски, сеть, терминал и т.д.). Ваша программа должна быть в состоянии проснуться и выполнять некоторую работу, когда результат исходит из любого из этих источников. Это можно сделать с помощью одного потока и системного вызова типа poll (2) или select (2). Когда ваш поток просыпается, он должен выяснить, какой результат пришел, найти, где он остановился в соответствующей задаче, и подобрать оттуда. Это много кода, который вам нужно написать.
Написание этого кода намного проще с использованием одного goroutine для каждой задачи. Затем состояние этой задачи фиксируется неявно в goroutine, и сбор, где он остановился, прост, как просыпаться и работать.
Ответ 2
Не эксперт в Go
(пока), но я бы сказал:
Всякий раз, когда это проще всего сделать.
Красота модели concurrency в Go
заключается в том, что она не является в основном многоядерной архитектурой с проверками и балансами, где вещи обычно ломаются - это многопоточная парадигма, которая не только хорошо вписывается в мульти -кратная архитектура, она также хорошо вписывается в распределенную архитектуру системы.
Вам не нужно принимать специальные меры для того, чтобы несколько goroutines
работали гармонично - они просто делают!
Здесь пример естественного параллельного алгоритма - я хочу объединить несколько каналов в один. Когда все входные каналы исчерпаны, я хочу закрыть выходной канал.
Просто использовать concurrency - на самом деле он даже не похож на concurrency - он выглядит почти процедурным.
/*
Multiplex a number of channels into one.
*/
func Mux(channels []chan big.Int) chan big.Int {
// Count down as each channel closes. When hits zero - close ch.
var wg sync.WaitGroup
wg.Add(len(channels))
// The channel to output to.
ch := make(chan big.Int, len(channels))
// Make one go per channel.
for _, c := range channels {
go func(c <-chan big.Int) {
// Pump it.
for x := range c {
ch <- x
}
// It closed.
wg.Done()
}(c)
}
// Close the channel when the pumping is finished.
go func() {
// Wait for everyone to be done.
wg.Wait()
// Close.
close(ch)
}()
return ch
}
Единственной концессией, которую я должен сделать для concurrency, является использование sync.WaitGroup
в качестве счетчика для параллельного подсчета.
Обратите внимание, что это не просто моя собственная работа - я очень помог с этим здесь.
Ответ 3
Также не эксперт в Go, поэтому некоторые из моих подходов могут быть неканоническими, но вот некоторые из способов, которые я нашел concurrency полезными до сих пор:
- Выполнение операций в ожидании запроса сети, ввода-вывода на диске или запроса базы данных для завершения
- Выполнение алгоритмов разделения и управления быстрее
- Поскольку goroutines являются функциями, а функции являются первоклассными гражданами в Go, вы можете передавать их как переменные. Это удобно, когда ваша программа имеет множество автономных элементов. (Например, я играю с симуляцией городской системы движения. Каждое транспортное средство - это собственная горутин, и они общаются с перекрестками и другими транспортными средствами, используя каналы. Каждый делает свою собственную вещь.)
- Одновременные операции ввода-вывода на разных устройствах
- Используется concurrency для выполнения алгоритма Дейкстры на множестве точек изображения, чтобы нарисовать линии "интеллектуальных ножниц" - один горутин на точку сделал эту реализацию значительно быстрее.
- GoConvey использует concurrency для одновременного запуска тестов по пакетам, чтобы быстрее реагировать на изменения при отладке с помощью веб-интерфейс. (В качестве присущего бонуса это добавляет некоторую псевдослучайность в последовательность тестирования, поэтому ваши результаты теста действительно соответствуют.)
Concurrency может быть (чтение: "иногда, но не обязательно всегда" ) полезно, когда у вас есть операции, которые могут выполняться независимо друг от друга, но в противном случае выполнялись бы последовательно. Даже если эти операции зависят от данных или какого-то сигнала от других goroutines в определенных точках, вы можете сообщить об этом всем с каналами.
Для некоторого вдохновения и важного различия в этих вопросах, а также для некоторых смешных изображений gopher см. Concurrency не Parallelism.
Ответ 4
Мои 2 цента... Если вы думаете о каналах /goroutines только в контексте concurrency, вам не хватает лодки.
В то время как go - это не язык объектов или строго функциональный язык, он позволяет вам использовать дизайнерские возможности и применить их.
Одним из основных принципов объектно-ориентированного проектирования является единоличная ответственность Principal. Применение этого принципа заставляет вас думать о дизайне с точки зрения сообщений, а не о сложном поведении объекта. Эти же конструктивные ограничения можно использовать в go, чтобы вы могли начать думать о "сообщениях на каналах", связывающих одноцелевые функции.
Это только один пример, но если вы начнете думать так, вы увидите еще много.