Чтение нескольких элементов из канала в Go

Я читаю значения из канала в цикле следующим образом:

for {
    capturedFrame := <-capturedFrameChan
    remoteCopy(capturedFrame)
}

Чтобы сделать его более эффективным, я хотел бы прочитать эти значения в пакете с чем-то вроде этого (псевдокод):

for {
    capturedFrames := <-capturedFrameChan
    multipleRemoteCopy(capturedFrames)
}

Но я не уверен, как это сделать. Если я позвоню capturedFrames := <-capturedFrameChan несколько раз, он заблокируется.

По сути, я хотел бы прочитать все доступные значения в captureFrameChan и, если они недоступны, он блокируется, как обычно.

Как это сделать в Go?


person laurent    schedule 25.10.2015    source источник
comment
Невозможно выполнить пакетное чтение значений из канала. Если вы не хотите вызывать multipleRemoteCopy несколько раз, просто буферизируйте значения локально.   -  person Stephan Dollberg    schedule 25.10.2015


Ответы (5)


Что-то вроде этого должно работать:

for {
    // we initialize our slice. You may want to add a larger cap to avoid multiple memory allocations on `append`
    capturedFrames := make([]Frame, 1)
    // We block waiting for a first frame
    capturedFrames[0] = <-capturedFrameChan

forLoop:
    for {
        select {
        case buf := <-capturedFrameChan:
            // if there is more frame immediately available, we add them to our slice
            capturedFrames = append(capturedFrames, buf)
        default:
            // else we move on without blocking
            break forLoop
        }
    }

    multipleRemoteCopy(capturedFrames)
}
person HectorJ    schedule 25.10.2015

Попробуйте это (для канала ch с типом T):

for firstItem := range ch {                      // For ensure that any batch could not be empty
    var itemsBatch []T
    itemsBatch = append(itemsBatch, firstItem)
Remaining: 
    for len(itemsBatch) < BATCHSIZE {            // For control maximum size of batch
        select {
        case item := <-ch:
            itemsBatch = append(itemsBatch, item)
        default:
            break Remaining
        }
    }
    // Consume itemsBatch here...
}

Но если BATCHSIZE является константой, этот код будет более эффективным:

var i int
itemsBatch := [BATCHSIZE]T{}
for firstItem := range ch {                      // For ensure that any batch could not be empty
    itemsBatch[0] = firstItem
Remaining:
    for i = 1; i < BATCHSIZE; i++ {              // For control maximum size of batch
        select {
        case itemsBatch[i] = <-ch:
        default:
            break Remaining
        }
    }
    // Now you have itemsBatch with length i <= BATCHSIZE;
    // Consume that here...
}
person Sorush Purabdi    schedule 17.08.2019

Используя len(capturedFrames), вы можете сделать это, как показано ниже:

for {
    select {
    case frame := <-capturedFrames:
        frames := []Frame{frame}
        for i := 0; i < len(capturedFrames); i++ {
           frames = append(frames, <-capturedFrames)
        }
        multipleRemoteCopy(frames)
    }
}
person Feiyu Zhou    schedule 05.05.2019

Кажется, вы также можете сравнить только

for {
    capturedFrame := <-capturedFrameChan
    go remoteCopy(capturedFrame)
}

без какого-либо рефакторинга кодовой базы, чтобы увидеть, повысит ли это эффективность.

person Uvelichitel    schedule 25.10.2015

В итоге я сделал это, как показано ниже. В основном я использовал len(capturedFrames), чтобы узнать, сколько кадров доступно, а затем извлек их в цикле:

for {
    var paths []string
    itemCount := len(capturedFrames)
    if itemCount <= 0 {
        time.Sleep(50 * time.Millisecond)
        continue
    }

    for i := 0; i < itemCount; i++ {
        f := <-capturedFrames
        paths = append(paths, f)
    }

    err := multipleRemoteCopy(paths, opts)
    if err != nil {
        fmt.Printf("Error: could not remote copy \"%s\": %s", paths, err)
    }
}
person laurent    schedule 06.12.2015