Пример для sync.WaitGroup правильный?

Правильно ли в этом примере использование sync.WaitGroup? Это дает ожидаемый результат, но я не уверен насчет wg.Add(4) и позиции wg.Done(). Имеет ли смысл добавлять четыре программы одновременно с помощью wg.Add()?

http://play.golang.org/p/ecvYHiie0P

package main

import (
    "fmt"
    "sync"
    "time"
)

func dosomething(millisecs time.Duration, wg *sync.WaitGroup) {
    duration := millisecs * time.Millisecond
    time.Sleep(duration)
    fmt.Println("Function in background, duration:", duration)
    wg.Done()
}

func main() {
    var wg sync.WaitGroup
    wg.Add(4)
    go dosomething(200, &wg)
    go dosomething(400, &wg)
    go dosomething(150, &wg)
    go dosomething(600, &wg)

    wg.Wait()
    fmt.Println("Done")
}

Результат (как и ожидалось):

Function in background, duration: 150ms
Function in background, duration: 200ms
Function in background, duration: 400ms
Function in background, duration: 600ms
Done
86 голосов | спросил topskip 6 +04002013-10-06T16:10:39+04:00312013bEurope/MoscowSun, 06 Oct 2013 16:10:39 +0400 2013, 16:10:39

3 ответа


0

Да, этот пример верный. Важно, чтобы wg.Add() происходило до go заявление, чтобы предотвратить условия гонки. Следующее также будет правильным:

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go dosomething(200, &wg)
    wg.Add(1)
    go dosomething(400, &wg)
    wg.Add(1)
    go dosomething(150, &wg)
    wg.Add(1)
    go dosomething(600, &wg)

    wg.Wait()
    fmt.Println("Done")
}

Однако бессмысленно снова и снова вызывать wg.Add, когда вы уже знаете, сколько раз он будет вызываться.


Waitgroups паникует, если счетчик падает ниже нуля. Счетчик начинается с нуля, каждый Done() является -1 и каждый Add() зависит от параметра. Таким образом, чтобы гарантировать, что счетчик никогда не опустится ниже и избежать паники, вам нужно Add(), чтобы гарантировано перед Done().

В Go такие гарантии предоставляются моделью памяти .

Модель памяти гласит, что все операторы в одной процедуре выполняются в том же порядке, в котором они написаны. Вполне возможно, что они не будут в таком порядке, но результат будет таким, как если бы он был. Также гарантируется, что программа не будет запущена до тех пор, пока не будет go оператор, который вызывает его . Поскольку Add() находится перед оператором go и оператор go встречается до Done() мы знаем, что Add() происходит до Done().

Если вы хотите, чтобы оператор go предшествовал Add(), программа может работать правильно. Однако, это было бы условием гонки, потому что это не было бы гарантировано.

ответил Stephen Weinberg 6 +04002013-10-06T16:34:41+04:00312013bEurope/MoscowSun, 06 Oct 2013 16:34:41 +0400 2013, 16:34:41
0

Я бы рекомендовал встраивать вызов wg.Add() в doSomething() сама функция, так что если вы настраиваете количество ее вызовов, вам не нужно отдельно настраивать параметр add вручную, что может привести к ошибке, если вы обновите один, но забудете обновить другой (в этот тривиальный пример маловероятен, но, тем не менее, я лично считаю, что это лучший способ для повторного использования кода).

Как отмечает Стивен Вайнберг в своем ответе на этот вопрос , вам нужно увеличить группу ожидания до для порождения gofunc, но вы можете легко сделать это, поместив spawn в gofunc внутри самой функции doSomething(), например так:

func dosomething(millisecs time.Duration, wg *sync.WaitGroup) {
    wg.Add(1)
    go func() {
        duration := millisecs * time.Millisecond
        time.Sleep(duration)
        fmt.Println("Function in background, duration:", duration)
        wg.Done()
    }()
}

Затем вы можете вызвать его без вызова go, например:

func main() {
    var wg sync.WaitGroup
    dosomething(200, &wg)
    dosomething(400, &wg)
    dosomething(150, &wg)
    dosomething(600, &wg)
    wg.Wait()
    fmt.Println("Done")
}

В качестве игровой площадки: http://play.golang.org/p/WZcprjpHa_ . /р>

ответил mroth 27 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowSun, 27 Sep 2015 18:26:22 +0300 2015, 18:26:22
0
  • небольшое улучшение на основе ответа Mroth
  • безопаснее использовать defer для Done
  func dosomething(millisecs time.Duration, wg *sync.WaitGroup) {
  wg.Add(1)
  go func() {
      defer wg.Done()
      duration := millisecs * time.Millisecond
      time.Sleep(duration)
      fmt.Println("Function in background, duration:", duration)
  }()
}

func main() {
  var wg sync.WaitGroup
  dosomething(200, &wg)
  dosomething(400, &wg)
  dosomething(150, &wg)
  dosomething(600, &wg)
  wg.Wait()
  fmt.Println("Done")
}
ответил Bnaya 17 J000000Tuesday18 2018, 20:39:53

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132