我想在另一个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..") }
这样做有什么陷阱吗?
服务每个http请求都在其自己的goroutine中运行。您可以从处理程序中启动新的goroutine,它们将并发运行,与执行处理程序的goroutine独立。
要注意的一些事情:
新的goroutine与处理程序goroutine独立运行。这意味着它可能在处理程序goroutine之前或之后完成,如果没有显式同步,则不能(不应)对此进行任何假设。
处理程序的http.ResponseWriter和http.Request参数只有在处理程序返回之前才有效且可以安全使用!这些值(或其中的“部分”)可以重复使用-这是一个实现细节,您也不应假设任何事情。处理程序返回后,您不应触摸(甚至不读取)这些值。
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.FormValue()
ParseMultiPartForm()
ParseForm()
Request.PostForm
Request.Form
所以,除非你同步够程,你不应该传递Request和ResponseWriter新够程,但是从需要采集数据Request的处理够程,并通过只如struct持有所需要的数据。
Request
ResponseWriter
struct
您的第二个示例:
foo := int64(0) bar := func() { // do slow things with foo } go bar()
很好 这是一个闭包,它引用的局部变量只要可以访问就可以保留。
请注意,您也可以将局部变量的值作为参数传递给匿名函数调用,如下所示:
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所做的更改是否需要对他人可见- 但这仍然需要同步,这将由通道解决方案取代)。
foo