Kok*_*zzu 2 http go goroutine server
我想在另一个goroutine中运行一些缓慢的例程,这样做是否安全:
func someHandler(w http.ResponseWriter, r *http.Request) {
go someReallySlowFunction() // sending mail or something slow
fmt.Fprintf(w,"Mail will be delivered shortly..")
}
func otherHandler(w http.ResponseWriter, r *http.Request) {
foo := int64(0)
bar := func() {
// do slow things with foo
}
go bar()
fmt.Fprintf(w,"Mail will be delivered shortly..")
}
Run Code Online (Sandbox Code Playgroud)
这样做有什么陷阱吗?
服务每个http请求都在其自己的goroutine中运行(有关此内容的更多详细信息)。您可以从处理程序中启动新的goroutine,它们将独立于执行处理程序的goroutine并发运行。
要注意的一些事情:
新的goroutine与处理程序goroutine独立运行。这意味着它可能在处理程序goroutine之前或之后完成,如果没有显式同步,则不能(不应)对此进行任何假设。
处理程序的http.ResponseWriter和http.Request参数仅在处理程序返回之前有效且可以安全使用!这些值(或其中的“部分”)可以重复使用-这是实现细节,您也不应假设任何内容。处理程序返回后,您不应触摸(甚至不读取)这些值。
处理程序返回后,将提交响应(或者可以随时提交响应)。这意味着您的新goroutine不应尝试使用http.ResponseWriterafter之后发送回任何数据。即使您没有触摸http.ResponseWriter处理程序中的,这也是正确的,即使没有从处理程序中惊慌也被视为对请求的成功处理,并因此将HTTP 200状态发送回(请参阅此示例)。
您可以将http.Request和http.ResponseWriter值传递给其他函数和新的goroutine,但必须小心:如果要从多个goroutine中读取/修改这些值,则应使用显式同步(例如,锁,通道)。从多个goroutine发送回数据)。
请注意,表面上看来,如果您的处理程序goroutine和新的goroutine都只读取/检查了http.Request,则可能仍然会出现问题。是的,多个goroutine可以读取同一变量而无需同步(如果没有人修改它)。但是调用的某些方法http.Request也会修改http.Request,并且如果没有同步,则不能保证其他goroutine从此更改中会看到什么。例如,Request.FormValue()返回与给定键关联的表单值。但是,此方法调用,ParseMultiPartForm()并且ParseForm()在必要时调用修改http.Request(例如,它们设置Request.PostForm和Request.Form结构字段)。
所以,除非你同步够程,你不应该传递Request和ResponseWriter新够程,但是从需要采集数据Request的处理够程,并通过只如struct持有所需要的数据。
您的第二个示例:
foo := int64(0)
bar := func() {
// do slow things with foo
}
go bar()
Run Code Online (Sandbox Code Playgroud)
很好 这是一个闭包,它引用的局部变量只要可以访问就可以保留。
请注意,您也可以将局部变量的值作为参数传递给匿名函数调用,如下所示:
foo := int64(0)
bar := func(foo int64) {
// do slow things with param foo (not the local foo var)
}
go bar(foo)
Run Code Online (Sandbox Code Playgroud)
在此示例中,匿名函数将查看并使用其参数,foo而不是局部变量foo。这可能是您想要的,也可能不是您想要的(取决于处理程序是否也使用foo和以及对任何goroutine所做的更改是否需要对他人可见-但这仍然需要同步,这将由通道解决方案取代)。