Назад к вопросам
Middle
100
questionbank
Какие правила следует учитывать при выделении переменной в горутине?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
// Правило 1: Избегайте совместного использования переменных без синхронизации.
// Горутины выполняются параллельно, и доступ к одной и той же переменной
// без блокировок или атомарных операций может привести к состояниям гонки (race conditions).
// Используйте sync.Mutex или каналы для безопасного доступа.
// Пример с race condition:
var counter int
func incrementBad() {
counter++ // Может быть не потокобезопасным
}
// Пример с Mutex:
import "sync"
var (
counterSafe int
mutex sync.Mutex
)
func incrementSafe() {
mutex.Lock()
defer mutex.Unlock()
counterSafe++
}
// Правило 2: Предпочитайте передачу данных между горутинами через каналы.
// Идиома Go: "Не передавайте данные по памяти, а передавайте владение памятью".
// Каналы обеспечивают синхронизацию и упорядочивают доступ к данным.
// Пример с каналом:
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
// Обработка задачи
results <- j * 2 // Отправка результата
}
}
// Правило 3: Будьте осторожны с замыканиями, захватывающими переменные из внешней области видимости.
// Горутина может начать выполнение позже, чем завершится цикл или функция, в которой она была создана.
// Если замыкание захватывает переменную по ссылке (что является поведением по умолчанию),
// оно может получить unexpected value. Копируйте значение переменной, если оно нужно внутри горутины.
// Пример с неожиданным поведением замыкания:
func badGoroutineLoop() {
for i := 0; i < 5; i++ {
go func() {
// К моменту запуска всех горутин, i скорее всего будет равно 5
// fmt.Println(i) // Output: 5 5 5 5 5
}()
}
}
// Пример с правильным захватом переменной:
func goodGoroutineLoop() {
for i := 0; i < 5; i++ {
iLocal := i // Создаем локальную копию переменной
go func() {
// fmt.Println(iLocal) // Output: 0 1 2 3 4 (порядок может варьироваться)
}()
}
}
// Правило 4: Используйте sync.WaitGroup для ожидания завершения всех запущенных горутин.
// Это позволяет избежать преждевременного завершения основной горутины (например, main),
// до того, как параллельные задачи будут выполнены.
// Пример с WaitGroup:
import "sync"
var wg sync.WaitGroup
func main() {
for i := 0; i < 3; i++ {
wg.Add(1) // Увеличиваем счетчик ожиданий
go func(id int) {
defer wg.Done() // Сообщаем о завершении работы горутины
// Работаем...
}(i)
}
wg.Wait() // Ожидаем, пока счетчик не станет равным 0
// Все горутины завершены
}