Go
golang

golang で semaphore

はじめに

  • chan を使った semaphore をループ処理のなかで使っていた
  • for 1ループごとに defer したいが func 抜ける時しか実行されない
  • そうだ、無名関数で囲もう

セマフォ

type Semaphore struct {
  sem chan struct{}
}

func NewSemaphore(size int) *Semaphore {
  return &Semaphore{
    sem: make(chan struct{}, size),
  }
}

func (s *Semaphore) V() {
  s.sem <- struct{}{}
}

func (s *Semaphore) P() {
  <-s.sem
}

普通にdefer

for {
  sem.V()
  defer sem.P() // 当然まともに動かない

  // 途中で抜ける
  if err != nil {
    continue
  }

  // 何か処理
}

無名関数で囲んでみる

for {
  func() {
    sem.V()
    defer sem.P()

    // 途中で抜けてもOK
    if err != nil {
      return
    }

    // 何か処理
  }()
}

Wrap関数にしてみる

type Semaphore struct {
  sem chan struct{}
}

func NewSemaphore(size int) *Semaphore {
  return &Semaphore{
    sem: make(chan struct{}, size),
  }
}

func (s *Semaphore) V() {
  s.sem <- struct{}{}
}

func (s *Semaphore) P() {
  <-s.sem
}

func (s *Semaphore) Wrap(f func() error) chan error {
  errChan := make(chan error, 1)

  s.sem <- struct{}{}
  errChan <- f()
  close(errChan)
  <-s.sem

  return errChan
}

最終形

for {

  ec := sem.Wrap(func() error {

    // 途中で抜けてもOK
    if err != nil {
      return
    }

    // 何か処理
  })

  if err := <- ec; err != nil {
    // エラー処理
  }

}