А что если горутины будут писать в одну и ту же строку/буфер — возможна ли такая проблема?

Да, можно, но при этом требуется обеспечить синхронизацию доступа для предотвращения гонок данных.

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

Что ответить на собеседовании

Да, можно, но при этом требуется обеспечить синхронизацию доступа для предотвращения гонок данных.

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

Ответ с пояснениями

Да, можно, но при этом требуется обеспечить синхронизацию доступа для предотвращения гонок данных.

Наиболее распространенные подходы:

  1. Мьютексы (sync.Mutex):

    import "sync"
    
    var buf []byte
    var mu sync.Mutex
    
    func processBuffer() {
        mu.Lock()
        defer mu.Unlock()
        // Безопасный доступ к buf
        // Например: buf = append(buf, data...)
    }
    
    func main() {
        // Инициализация buf
        go processBuffer()
        go processBuffer()
        // ...
    }
    
  2. Каналы (chan):

    Можно передавать ownership буфера между горутинами или использовать канал для координации доступа.

    import "bytes"
    
    type BufferAccess struct {
        buf *bytes.Buffer // Указатель на буфер для совместного использования
        mu  sync.Mutex    // Мьютекс для синхронизации доступа
    }
    
    func (ba *BufferAccess) Write(p []byte) (n int, err error) {
        ba.mu.Lock()
        defer ba.mu.Unlock()
        return ba.buf.Write(p)
    }
    
    func worker(ba *BufferAccess, data []byte) {
        ba.Write(data) // Безопасная запись через метод с мьютексом
    }
    
    func main() {
        sharedBuffer := &BufferAccess{buf: &bytes.Buffer{}}
        // Запуск горутин, передавая sharedBuffer
        go worker(sharedBuffer, []byte("hello"))
        go worker(sharedBuffer, []byte("world"))
        // ...
    }
    
  3. Пул буферов (sync.Pool):

    Менее острая необходимость в синхронизации доступа к самому буферу, если каждая горутина берет буфер из пула, использует его, а затем возвращает.

    import (
        "bytes"
        "sync"
    )
    
    var bufferPool = sync.Pool{
        New: func() interface{} {
            // Размер буфера по умолчанию
            return bytes.NewBuffer(make([]byte, 0, 1024))
        },
    }
    
    func processData(data []byte) {
        buf := bufferPool.Get().(*bytes.Buffer)
        defer bufferPool.Put(buf) // Обязательно вернуть буфер в пул
    
        buf.Reset() // Очистить буфер перед использованием
        buf.Write(data)
        // Обработка данных в буфере
        // ...
    }
    
    func main() {
        go processData([]byte("message1"))
        go processData([]byte("message2"))
        // ...
    }
    

Важно: Использование буфера без синхронизации в конкурентной среде приведет к состоянию гонки данных, что является причиной непредсказуемого поведения программы и сложно отлавливаемых ошибок.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее