- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我一直在阅读一些关于使用golang的context包的文章。我最近在博客中看到以下文章:http://p.agnihotry.com/post/understanding_the_context_package_in_golang/
文章对 go 中的上下文取消函数进行了以下说明:
"You can pass around the cancel function if you wanted to, but, thatis highly not recommended. This can lead to the invoker of cancel notrealizing what the downstream impact of canceling the context may be.There may be other contexts that are derived from this which may causethe program to behave in an unexpected fashion. In short, NEVER passaround the cancel function."
但是,如果我希望激活父 context.Done() channel ,将取消函数作为参数传递似乎是唯一的选择(请参阅下面的代码片段)。例如,下面代码片段中的代码 Done channel 仅在执行 function2 时激活。
package main
import (
"context"
"fmt"
"time"
)
func function1(ctx context.Context) {
_, cancelFunction := context.WithCancel(ctx)
fmt.Println("cancel called from function1")
cancelFunction()
}
func function2(ctx context.Context, cancelFunction context.CancelFunc) {
fmt.Println("cancel called from function2")
cancelFunction()
}
func main() {
//Make a background context
ctx := context.Background()
//Derive a context with cancel
ctxWithCancel, cancelFunction := context.WithCancel(ctx)
go function1(ctxWithCancel)
time.Sleep(5 * time.Second)
go function2(ctxWithCancel, cancelFunction)
time.Sleep(5 * time.Second)
// Done signal is only received when function2 is called
<-ctxWithCancel.Done()
fmt.Println("Done")
}
那么,传递这个取消函数实际上是一个问题吗?是否有与使用 context 包及其取消功能相关的最佳实践?
最佳答案
在您的具体示例中,代码量足够小,理解其工作原理可能没有问题。当您替换function1
时,问题就开始了。和function2
与更复杂的事情。您链接到的文章给出了为什么传递取消上下文可以做一些难以推理的事情的具体原因,但更一般的原则是您应该尝试将协调工作(取消、旋转 goroutine)从底层工作中分离出来尽可能多地完成(无论 function1
和 function2
正在做什么)。这只会有助于更轻松地独立推理代码的子部分,并有助于使测试变得更容易。 “function2
does
function2
does
function1
协调”更容易理解。
而不是将取消函数传递给function2
,你可以在你生成的 goroutune 中调用它来运行 function2
:
func main() {
//...
go func() {
function2(ctxWithCancel)
cancelFunction()
}()
//...
}
这是侄女,因为确定何时取消的协调工作全部包含在调用函数中,而不是分散在多个函数中。
如果你想拥有function2
有条件地取消上下文,让它显式返回某种值来指示是否发生了某些可取消的条件:
func function2(ctx context.Context) bool {
//...
if workShouldBecanceled() {
return true
}
//...
return false
}
func main() {
//...
go func() {
if function2(ctxWithCancel) {
cancelFunction()
}
}()
//...
}
这里我使用了一个 bool 值,但这种模式通常与 error
一起使用。 s - 如果function2
返回非零 error
,取消剩下的工作。
根据您在做什么,类似 errgroup.WithContext
可能对你有用。这可以协调多个并发操作,所有这些操作都可能失败,并在第一个操作失败后立即取消其他操作。
我在上下文取消方面尝试遵循的另一个最佳实践:始终确保在某个时刻调用取消函数。来自 docs ,调用取消函数两次是安全的,所以我经常这样做:
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
//...
if shouldCancel() {
cancel()
}
//...
}
编辑回复评论:
如果您有多个长时间运行的操作(例如服务器、连接等),并且您希望在第一个操作停止后立即关闭所有操作,则上下文取消是一种合理的方法去做。但是,我仍然建议您在单个函数中处理所有上下文交互。像这样的事情会起作用:
func operation1(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
}
//...
}
}
func operation2(ctx context.Context) {
// Similar code to operatoin1()
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
defer cancel()
operation1(ctx)
}()
go func() {
defer wg.Done()
defer cancel()
operation2(ctx)
}()
wg.Wait()
}
一旦其中一个操作终止,另一个操作就会被取消,但是 main
仍然等待两者完成。两个操作都不需要担心管理这个问题。
关于go - Go 上下文取消函数的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70042646/
我正在使用 go 图表库 https://github.com/wcharczuk/go-chart制作条形图。我面临的问题是标签值很长,我想将文本旋转 45 度以显示完整文本 我喜欢显示的日期格式是
我在构建一个非常简单的通过 cgo 调用 c 代码的 go 程序时遇到了问题。我的设置: $: echo $GOPATH /go $: pwd /go/src/main $: ls ctest.c
没有 C 的背景,只有 Go 的“初学者”经验,我正在尝试弄清楚 main.go 是实际需要的还是只是一个约定。 我想创建一个简单的网络 API,但有人可以为我澄清一下吗? 最佳答案 main.go
我read从 Go 1.4 开始,Go 运行时是用 Go 本身编写的(而不是用 C)。 这怎么可能?如果 Go 程序在运行时之上运行,并且运行时是 Go 程序,那么运行时是否在自身之上运行? 最佳答案
这是“Go 之旅”中的代码示例 Range and Close : package main import ( "fmt" ) func fibonacci(n int, c chan int
给定以下 go.mod 文件: module foo go 1.12 require ( github.com/bar/baz v1.0.0 github.com/rat/cat v1
我有一个 CI/CD 管道,它需要跨平台并与几个不同的管理程序一起工作。为了不必更改 Windows 和 Linux 的构建任务,我认为 Go 将是编写一次代码并在任何地方运行的好方法。然而,考虑到
我有一个 Dockerfile,用于使用 go build 编译 Go 应用程序。我进行了研究,确实建议将 go build 用于生产。 但是我找不到正确的答案来解释为什么。 我了解 go run 创
我尝试在命令提示符#Go lang 中运行该程序-但是当我键入运行“go run hello.go”命令时,我开始了 CreateFile hello.go:The system cannot fin
我正在使用“Go 编程语言”一书学习 Go。第一章介绍os.Open用于读取文件的模块。我尝试打开如下所示的 go 文件。 f, err = os.Open("helloworld.go") 我收
关闭。这个问题需要details or clarity .它目前不接受答案。 想改进这个问题?通过 editing this post 添加详细信息并澄清问题. 2年前关闭。 Improve this
为了解决我对 goroutine 的一些误解,我去了 Go 操场跑了 this code : package main import ( "fmt" ) func other(done cha
这个问题在这里已经有了答案: Evaluate/Execute Golang code/expressions like js' eval() (5 个回答) 1年前关闭。 对于任何 go 程序,我想
这是我基本上试图从路径打印基准的代码。 这意味着,如果用户输入“/some/random/path.java”,则输出将为“path”。同样,如果用户arg为“/another/myapp.c”,则输
$ go version 1.13.3 我的文件夹结构如下: GOPATH +---src +--- my-api-server +--- my-auth-server
这个问题在这里已经有了答案: How to embed file for later parsing execution use (4 个答案) What's the best way to bun
我觉得这有点奇怪,为什么这段代码不起作用? package main import "fmt" func main() { var i, j int = 1, 2 k
go编译器执行完如下命令后的可执行文件存放在哪里? $> go run file.go 最佳答案 在 /tmp 文件夹中,如果您使用的是 unix 机器。 如果您使用的是 Windows,则在 \Us
我目前正在开始使用 Go,并且已经深入研究了有关包命名和工作区文件夹结构的注意事项。 不过,我不太确定如何根据 Go 范式正确组织我的代码。 这是我当前的结构示例,它位于 $GOPATH/src 中:
假设我有一个接受用户输入的 Lua 程序,而该输入恰好是有效的 Lua 源代码。这是在程序仍在运行时进行清理、编译和执行的。 Go 是否(或将)实现这样的事情? 最佳答案 我认为以下两个项目之间有足够
我是一名优秀的程序员,十分优秀!