我想在另一个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..")
}
这样做有什么问题吗?
答案 0 :(得分:6)
每个http请求的服务都在自己的goroutine(more details on this)中运行。您可以从处理程序中启动新的goroutine,它们将同时运行,独立于执行处理程序的goroutine。
要注意的一些事项:
新的goroutine独立于处理程序goroutine运行。这意味着它可以在处理程序goroutine之前或之后完成,如果没有明确的同步,你就不能(不应该)假设这一点。
处理程序的http.ResponseWriter
和http.Request
参数只有在处理程序返回之前才有效并且可以安全使用!这些值(或#34;部分")可以重复使用 - 这是一个实现细节,你也不应该假设任何东西。处理程序返回后,您不应触摸(甚至不读取)这些值。
处理程序返回后,响应将被提交(或者可以随时提交)。这意味着您的新goroutine不应尝试在此之后使用http.ResponseWriter
发回任何数据。这是正确的,即使您不接触处理程序中的http.ResponseWriter
,也不会将处理程序中的错误视为成功处理请求,因此会发回HTTP 200状态({ {3}})。
您可以将http.Request
和http.ResponseWriter
值传递给其他函数和新goroutine,但必须注意:如果您打算使用显式同步(例如锁,通道)从多个goroutine读取/修改这些值(或者您想从多个goroutine发回数据)。
请注意,如果您的处理程序goroutine和您的新goroutine都只是读取/检查http.Request
,那么这仍然可能会有问题。是的,多个goroutine可以在没有同步的情况下读取相同的变量(如果没有人修改它)。但调用http.Request
的某些方法也会修改http.Request
,如果没有同步,则无法保证其他goroutine会从此更改中看到什么。例如,see an example of this返回与给定键关联的表单值。但是,如果需要,此方法会调用Request.FormValue()
和ParseMultiPartForm()
修改http.Request
(例如,他们设置Request.PostForm
和Request.Form
结构字段。)
因此,除非您同步goroutine,否则不应将Request
和ResponseWriter
传递给新的goroutine,而是从处理程序goroutine中的Request
获取所需的数据,并仅传递例如保留所需数据的struct
。
你的第二个例子:
foo := int64(0)
bar := func() {
// do slow things with foo
}
go bar()
这很好。这是一个ParseForm()
,只要它们可以访问,它所引用的局部变量就会存在。
请注意,您也可以将局部变量的值作为参数传递给匿名函数调用,如下所示:
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所做的更改是否需要对另一个可见 - 但这仍然需要同步,这将是被渠道解决方案取代。)
答案 1 :(得分:1)
如果您关心邮件的确认,那么发布的代码将无法提供帮助。在单独的goroutine中运行代码使其独立,即使由于goroutine函数中的某些错误而未发送邮件,服务器回复也会成功。