下面是我在 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
通道已关闭并继续读取尚未更新的字段?
如果您能向我解释我的信念是否仍然正确,我将非常感激。如果错误,请告诉我同步字段读写的正确方法。
对
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
}