Можно ли использовать один и тот же буфер []byte в нескольких горутинах?go-89

Ответ на этот вопрос требует понимания принципов конкурентности в Go и работы с памятью:

Краткий ответ:

Да, можно, но с важными оговорками

Подробный анализ

Безопасные сценарии использования:

  1. Чтение без модификации (только для чтения)
buf := make([]byte, 1024)

// Безопасно в нескольких горутинах
go func() {
    fmt.Println(buf[0]) // Чтение
}()

go func() {
    fmt.Println(buf[1]) // Чтение
}()
  1. Атомарные операции с целыми буферами
var bufPool = sync.Pool{
    New: func() interface{} {
        return make([]byte, 1024)
    }
}

// Безопасно - каждая горутина получает свой буфер
go func() {
    b := bufPool.Get().([]byte)
    defer bufPool.Put(b)
    // работа с b
}()

Опасные сценарии:

  1. Конкурентная запись (data race)
buf := make([]byte, 10)

go func() {
    buf[0] = 1 // Опасность гонки данных!
}()

go func() {
    buf[1] = 2 // Опасность гонки данных!
}()
  1. Изменение размера (re-slicing)
buf := make([]byte, 0, 10)

go func() {
    buf = append(buf, 1) // Гонка данных при изменении slice header!
}()

Правильные подходы для совместного использования:

  1. Использование мьютексов
var (
    buf  = make([]byte, 1024)
    bufMu sync.Mutex
)

go func() {
    bufMu.Lock()
    buf[0] = 1
    bufMu.Unlock()
}()
  1. Выделение отдельных регионов буфера
buf := make([]byte, 2048)

go func() {
    part := buf[:1024] // Первая половина
    // Работа с part
}()

go func() {
    part := buf[1024:] // Вторая половина
    // Работа с part
}()
  1. Каналы для передачи владения
bufChan := make(chan []byte, 1)
bufChan <- make([]byte, 1024)

go func() {
    b := <-bufChan
    b[0] = 1 // Эксклюзивный доступ
    bufChan <- b
}()

Критические моменты:

  1. Slice header (len, cap, указатель) может быть поврежден при конкурентном изменении
  2. Алиасинги - если несколько горутин получают ссылки на одни данные
  3. False sharing - проблемы с кешем CPU при работе с соседними участками памяти

Производительность vs Безопасность

// Плохо для производительности, но безопасно
var globalBuf struct {
    sync.Mutex
    data []byte
}

// Лучше - локальные буферы + редкая синхронизация
func worker(localBuf []byte) {
    // ...
}

Резюмируем

использовать общий буфер в нескольких горутинах можно, но только либо для чтения, либо с надлежащей синхронизацией. Для записи безопаснее выделять отдельные буферы или части буфера. Всегда проверяйте код на гонки данных с помощью go run -race.