下面是我在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(),
}
}
}
func (p *promise) changeState(from, to State) bool {
return atomic.CompareAndSwapInt32(&p.state, int32(from), int32(to))
}
一位同事今天给了我一个Go Memory Model文章的链接。在文章中,作者包括以下示例,并提到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
通道已关闭,并继续读取尚未更新的字段?
如果你能向我解释我的信念是否仍然正确,我将非常感激。如果它是错误的,请告诉我正确的方法来同步读&写一个字段。
1条答案
按热度按时间s71maibg1#
对
p.outcome
的赋值是“在close(done)
之前排序”的,因此任何检测到done
被关闭的goroutine都会看到p.outcome
的最新值,因为如果done
被关闭,p.outcome
发生在它之前。p.changeState
可能有一个比赛,但你没有包括在你的帖子。也就是说,通道和goroutine提供了与promise相同的功能,并且以更干净的方式实现: