Почему результат не такой, как ожидалось с флагом "-race"? - PullRequest
0 голосов
/ 26 апреля 2018

Почему результат не такой, как ожидалось с флагом "-race"? Ожидается тот же результат: 1000000 - с флагом «-race» и без этого

https://gist.github.com/romanitalian/f403ceb6e492eaf6ba953cf67d5a22ff

package main

import (
    "fmt"
    "runtime"
    "sync/atomic"
    "time"
)

//$ go run -race main_atomic.go
//954203
//
//$ go run main_atomic.go
//1000000

type atomicCounter struct {
    val int64
}

func (c *atomicCounter) Add(x int64) {
    atomic.AddInt64(&c.val, x)
    runtime.Gosched()
}

func (c *atomicCounter) Value() int64 {
    return atomic.LoadInt64(&c.val)
}

func main() {
    counter := atomicCounter{}

    for i := 0; i < 100; i++ {
        go func(no int) {
            for i := 0; i < 10000; i++ {
                counter.Add(1)
            }
        }(i)
    }

    time.Sleep(time.Second)
    fmt.Println(counter.Value())
}

1 Ответ

0 голосов
/ 27 апреля 2018

Причина, по которой результат не совпадает, заключается в том, что time.Sleep(time.Second) не гарантирует, что все ваши процедуры будут выполнены за промежуток времени в одну секунду. Даже если вы выполните go run main.go, это не гарантирует, что вы будете получать один и тот же результат каждый раз. Вы можете проверить это, если вместо time.Second поставить time.Milisecond, вы увидите гораздо более противоречивые результаты.

Какое бы значение вы ни указали в методе time.Sleep, это не гарантирует выполнения всех ваших подпрограмм, это просто означает, что менее вероятно, что все ваши подпрограммы не завершатся вовремя.

Для получения стабильных результатов вы захотите немного синхронизировать ваши программы. Вы можете использовать WaitGroup или каналы.

С WaitGroup:

//rest of the code above is the same
func main() {
    counter := atomicCounter{}
    var wg sync.WaitGroup
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(no int) {
            for i := 0; i < 10000; i++ {
                counter.Add(1)
            }
            wg.Done()
        }(i)
    }

    wg.Wait()
    fmt.Println(counter.Value())
}

С каналами:

func main() {
    valStream := make(chan int)
    doneStream := make(chan int)
    result := 0
    for i := 0; i < 100; i++ {
        go func() {
            for i := 0; i < 10000; i++ {
                valStream <- 1
            }
            doneStream <- 1
        }()
    }

    go func() {
        counter := 0
        for count := range doneStream {
            counter += count
            if counter == 100 {
                close(doneStream)
            }
        }
        close(valStream)
    }()

    for val := range valStream {
        result += val
    }
    fmt.Println(result)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...