среда, 9 октября 2019 г.

Использование sync.Once для однократного вызова функций из конкурентных go-процедур

Для понимания работы примера из поста о синглтон паттерне рассмотрим тип sync.Once и его метод Do.

Once это объект, который выполнит только однократное действие. В приведенном ниже примере запускается 10 go-процедур, вызывающих функцию, печатающую строку "Only once", но за счет использования sync.Once, несмотря на 10 вызовов, функция выполнится лишь однократно.

package main

import (
  "fmt"
  "sync"
)

func main() {
  var once sync.Once
  onceBody := func() {
    fmt.Println("Only once")
  }
  done := make(chan bool)
  for i := 0; i < 10; i++ {
    go func() {
      once.Do(onceBody)
      done <- true
    }()
  }
  for i := 0; i < 10; i++ {
    <-done
  }
}

Вывод

Only once

Запустить в песочнице play.golang.org

Метод Do для типа Once

func (o *Once) Do(f func())

Метод Do вызывает функцию f только если Do был вызван впервые для этого инстанса Once. Другими словами для данной переменной

var once Once

если once.Do(f) вызывается несколько раз, только первый вызов выполнит f, даже если f имеет различное значение в каждом вызове. Если требуется выполнять f для каждого вызова, тогда необходим новый экземпляр Once на каждый вызов.

Do предназначен для инициализации, которая должна быть запущена ровно один раз. Поскольку f является неликадным, может потребоваться использование литерала функции для захвата аргументов функции, которая вызывается Do:

config.once.Do(func() { config.init(filename) })

Поскольку никакой вызов Do не возвращается до тех пор, пока не вернется один вызов f, если f вызовет Do, это приведет к взаимоблокировке.

Если f вызывает panic, Do считает, что f выполнила возврат (return); последующие вызовы Do будут возвращаться без вызова f.


Читайте также:


Комментариев нет:

Отправить комментарий