英文:
Order of execution in Golang involving defer and goroutine
问题
我有一个易于遵循的日志记录情况,似乎发生了错误的顺序。
如图所示,第一行的开始时间早于第二行的开始时间。然而,第二行的执行完全包含了第一行所代表的执行,所以这似乎是不可能的。
首先执行以下代码:
defer func(begin time.Time) {
p.logger.InfoV(ctx, "Properties Listsearch "+method,
zap.Any("searchRequest", ctx.Request.URL.Query()),
zap.Float64("elapsed", time.Now().Sub(begin).Round(time.Millisecond).Seconds()),
)
}(time.Now())
然后,大约40行之后,调用一个包含以下代码的函数:
go func() {
companyFacetsStartTime := time.Now()
// 实际工作在这里发生
p.logger.DebugV(ctx, "company facets query complete", zap.Float64("elapsed", time.Now().Sub(companyFacetsStartTime).Round(time.Millisecond).Seconds()))
}()
我正在查看包含相同事务ID的日志,所以这两个调用必须有关联。
第一行的开始时间在第二行的开始时间之前似乎是不可能的。第一个代码块中的延迟执行是否有什么使其 time.Now() 比第二个代码块中的 time.Now() 执行得晚的特殊方式?如果没有,是否还有其他解释?
英文:
I have an easy-to-follow logging situation that seems to be occurring in the wrong order.
As you can see, the start time of the first row occurs before the start time of the second row. Yet the execution of the second row fully encapsulates the execution represented by the first row, so this doesn't seem possible.
This code executes first:
defer func(begin time.Time) {
p.logger.InfoV(ctx, "Properties Listsearch "+method,
zap.Any("searchRequest", ctx.Request.URL.Query()),
zap.Float64("elapsed", time.Now().Sub(begin).Round(time.Millisecond).Seconds()),
)
}(time.Now())
Then, about 40 lines later, a function is called which contains this code:
go func() {
companyFacetsStartTime := time.Now()
// actual work occurs here
p.logger.DebugV(ctx, "company facets query complete", zap.Float64("elapsed", time.Now().Sub(companyFacetsStartTime).Round(time.Millisecond).Seconds()))
}()
I am looking at logs that contain the same transactionID's, so these two calls have to be related.
It doesn't seem possible for row 1's Start to come before row 2's Start. Is there something about the way the defer in the first code block is executed that makes its time.Now() execute later than the time.Now() in the second code block? If not, is there any other explanation?
答案1
得分: 2
从你的评论中Shouldn't time.Now() in the parameter list be set when the defer is executed
- 答案是否定的。
defer
函数的参数在遇到defer
语句时进行评估 - 即使defer
函数直到容器函数完成才会执行。
引用自Go语言规范:
> 每次执行“defer”语句时,函数值和调用的参数都会像往常一样进行评估并保存,但实际的函数不会被调用。
所以在你的例子中:
defer func(begin time.Time) {
p.logger.InfoV(ctx, "Properties Listsearch "+method,
zap.Any("searchRequest", ctx.Request.URL.Query()),
zap.Float64("elapsed", time.Now().Sub(begin).Round(time.Millisecond).Seconds()),
)
}(time.Now()) // time.Now() 在处理此defer语句时被收集...
// ... 即使执行是稍后进行的。
由于你想要捕获容器函数的经过时间,这实际上是你想要捕获的正确的 time.Now()
。
如果你在defer
代码内部评估begin
,报告的持续时间将仅记录评估zap日志参数的经过时间。
英文:
From your comment Shouldn't time.Now() in the parameter list be set when the defer is executed
- the answers is no.
defer
function parameters are evaluated when the defer
statement is encountered - even though the defer
function won't be executed until the container function's completion.
Quoting from the go language spec
> Each time a "defer" statement executes, the function value and
> parameters to the call are evaluated as usual and saved anew but the
> actual function is not invoked.
so in your example:
defer func(begin time.Time) {
p.logger.InfoV(ctx, "Properties Listsearch "+method,
zap.Any("searchRequest", ctx.Request.URL.Query()),
zap.Float64("elapsed", time.Now().Sub(begin).Round(time.Millisecond).Seconds()),
)
}(time.Now()) // time.Now() is collected when this defer statement is processed ...
// ... even though execution is later.
Since you want to capture elapsed time of the container function, this is actually the correct time.Now()
you want to capture.
If you evaluated begin
from within the defer
code, the duration reported would simply just record the elapsed time to evaluate the zap log's parameters.
答案2
得分: -1
我通过复制日志时间戳并将其标记为“开始时间”混淆了情况。这导致我错误地解释了事情的顺序,这就是为什么我发布了这个问题。
整个问题非常混乱,可能应该删除,而不是浪费其他人在SO上的时间。这样做是正确的吗?
对于整个混乱我表示道歉。
英文:
I confused the situation by copying the logging timestamp and labeling it as Start time. That led to my misinterpreting the order of things, which is why I posted this question.
This whole question is a confusing mess that probably should be deleted, rather than waste other peoples' time on SO. Is that the right thing to do?
I apologize for this whole mess.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论