使用 context.Err() 防止竞争条件

时间:2021-04-05 22:12:43

标签: go

据我所知,在 Go 中使用上下文时,检查上下文是否被取消或达到最后期限的正确方法是在相关代码之后调用 context.Err()。所以像:

func myFunc(ctx context.Context) {
    // call some context-aware functionality
    result, err := SomeContextAwareFunc(ctx)

    // check if we hit a deadline or cancellation
    if ctxErr := ctx.Err(); ctxErr != nil {
        // handle an expired and/or cancelled context here
    }

    // process the result
    _ = result
}

不过,如果我没记错的话,这代表了一种竞争条件。上下文可能在 SomeContextAwareFunc 之后但在 Err 函数被调用之前过期。在这种情况下,我们会认为上下文过期会缩短 SomeContextAwareFunc 但实际上并没有(例如,我们可以使用结果)。

我在操场上测试了这个,在函数返回和调用 Err 之间有一个人为的延迟,它确实错误地指示了一个过期的上下文。 https://play.golang.org/p/Rd-fhWOW-AB

防止这种情况的正确方法是什么?上下文感知函数是否必须始终返回错误,以便调用者仅在返回错误时检查上下文?

1 个答案:

答案 0 :(得分:2)

如果 SomeContextFunction 返回上下文错误或 wraps 上下文错误,则使用此代码确定 SomeContextFunction 是否因截止日期或取消而返回:

result, err := SomeContextAwareFunc(ctx)
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
     // deadline exceeded or canceled
} else if err != nil {
     // some other eror
}

因为在 SomeContextAwareFunc 返回后可能会过期或取消上下文,所以测试 SomeContextAwareFunc 的错误返回是确定函数失败原因的唯一方法。