为每个 http 请求提供服务在其自己的 goroutine 中运行(有关于此的更多详细信息)。您可以从处理程序启动新的 goroutine,并且它们将同时运行,独立于执行处理程序的 goroutine。
需要注意的一些事项:
新的 goroutine 独立于处理程序 goroutine 运行。这意味着它可能在处理程序 goroutine 之前或之后完成,如果没有显式同步,您不能(不应该)假设与此相关的任何内容。
The http.ResponseWriter and http.Request处理程序的参数仅在处理程序返回之前才有效且可以安全使用!这些值(或其中的“部分”)可以重复使用 - 这是一个实现细节,您也不应该假设任何内容。一旦处理程序返回,您不应触摸(甚至不读取)这些值。
一旦处理程序返回,响应就会被提交(或者可以随时提交)。这意味着你的新 goroutine 不应尝试使用以下方法发回任何数据http.ResponseWriter
在这之后。即使您不触摸http.ResponseWriter
在您的处理程序中,处理程序不会出现恐慌被视为成功处理了请求,因此会发回 HTTP 200 状态(看一个例子).
您可以通过http.Request
and http.ResponseWriter
其他函数和新 Goroutines 的值,但必须小心:如果您打算从多个 Goroutines 读取/修改这些值(或者您想从多个 Goroutines 发回数据),则应该使用显式同步(例如锁、通道) 。
请注意,看起来如果您的处理程序 Goroutine 和新的 Goroutine 都只是读取/检查http.Request
,这仍然可能有问题。是的,多个 goroutine 可以读取同一个变量而无需同步(如果没有人修改它)。但调用某些方法http.Request
还修改http.Request
,如果没有同步,就无法保证其他 goroutine 会从这个变化中看到什么。例如Request.FormValue()返回与给定键关联的表单值。但这个方法调用ParseMultiPartForm() and ParseForm()如有必要,修改http.Request
(例如,他们设置了Request.PostForm
and Request.Form
结构字段)。
所以除非你同步你的 goroutine,否则你不应该通过Request
and ResponseWriter
到新的 goroutine,但从新的 goroutine 获取所需的数据Request
在处理程序 goroutine 中,并且仅传递例如Astruct
保存所需的数据。
你的第二个例子:
foo := int64(0)
bar := func() {
// do slow things with foo
}
go bar()
这完全没问题。这是一个closure,并且它所引用的局部变量只要可访问就会一直存在。
请注意,您也可以将局部变量的值作为参数传递给匿名函数调用,如下所示:
foo := int64(0)
bar := func(foo int64) {
// do slow things with param foo (not the local foo var)
}
go bar(foo)
在此示例中,匿名函数将查看并使用其参数foo
而不是局部变量foo
。这可能是也可能不是您想要的(取决于处理程序是否也使用foo
以及任何一个 goroutine 所做的更改是否需要对另一个 goroutine 可见 - 但这无论如何都需要同步,这将被通道解决方案取代)。