Go 内存模型:如何通过正确的同步将值分配给字段

问题描述 投票:0回答:1

下面是我在 Go 中实现

Promise
的核心部分。

// A promise represents the future result of a call to a function.
type promise struct {
    // state is the current state of this promise.
    state int32
    // done is closed when execution completes to unblock concurrent waiters.
    done chan struct{}
    // the function that will be used to populate the outcome.
    function Function
    // outcome is set when execution completes.
    outcome Outcome
}

// get returns the value associated with a promise.
//
// All calls to promise.get on a given promise return the same result
// but the function is called (to completion) at most once.
//
// - If the underlying function has not been invoked, it will be.
// - If ctx is cancelled, get returns (nil, context.Canceled).
func (p *promise) get(ctx context.Context) Outcome {
    if ctx.Err() != nil {
        return Outcome{
            Value: nil,
            Err:   ctx.Err(),
        }
    }

    if p.changeState(IsCreated, IsExecuted) {
        return p.run(ctx)
    }

    return p.wait(ctx)
}

// run starts p.function and returns the result.
func (p *promise) run(ctx context.Context) Outcome {
    go func() {
        v, err := doExecute(ctx, p.function)

        p.outcome = Outcome{
            Value: v,
            Err:   err,
        }
        p.function = nil // aid GC
        close(p.done)
    }()

    return p.wait(ctx)
}

// wait waits for the value to be computed, or ctx to be cancelled.
func (p *promise) wait(ctx context.Context) Outcome {
    select {
    case <-p.done:
        return p.outcome

    case <-ctx.Done():
        return Outcome{
            Value: nil,
            Err:   ctx.Err(),
        }
    }
}

一位同事今天给了我一个指向 Go 内存模型 文章的链接。在文章中,作者提供了以下示例,并提到

g
可以打印
2
,然后打印
0

var a, b int

func f() {
    a = 1
    b = 2
}

func g() {
    print(b)
    print(a)
}

func main() {
    go f()
    g()
}

存在竞争的程序是不正确的,并且可能表现出非顺序一致的执行。特别要注意的是,读 r 可能会观察到与 r 并发执行的任何写 w 写入的值。即使发生这种情况,也不意味着在 r 之后发生的读取会观察到在 w 之前发生的写入。

到目前为止,我一直认为在关闭

done
通道之前设置一个变量可以保证其他例程能够看到该变量的最新值。

然而,上面的例子让我质疑我对 Go 工作原理的理解,以及使用

done
通道是否会产生任何差异。其他例程是否可以检测到
done
通道已关闭并继续读取尚未更新的字段?

如果您能向我解释我的信念是否仍然正确,我将非常感激。如果错误,请告诉我同步字段读写的正确方法。

go memory concurrency synchronization
1个回答
0
投票

p.outcome
的赋值是“在”
close(done)
之前排序的,因此任何检测到
done
关闭的 goroutine 都会看到
p.outcome
的最新值,因为如果
done
关闭,则
p.outcome
发生在之前它。

p.changeState
可能有一场比赛,但您没有将其包含在您的帖子中。

也就是说,通道和 goroutine 提供了与 Promise 相同的功能,并且以更简洁的方式实现:

resultCh:=make(chan resultType)
go func() {
   resultCh<-someFunc(ctx)
}()

select {
   case <-ctx.Done():
     // Canceled
   case result:=<-resultCh:
     // result is ready
}
© www.soinside.com 2019 - 2024. All rights reserved.