一尘不染

Webhook进程在另一个goroutine上运行

go

我想在另一个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..")
}

这样做有什么陷阱吗?


阅读 196

收藏
2020-07-02

共1个答案

一尘不染

服务每个http请求都在其自己的goroutine中运行。您可以从处理程序中启动新的goroutine,它们将并发运行,与执行处理程序的goroutine独立。

要注意的一些事情:

  • 新的goroutine与处理程序goroutine独立运行。这意味着它可能在处理程序goroutine之前或之后完成,如果没有显式同步,则不能(不应)对此进行任何假设。

  • 处理程序的http.ResponseWriterhttp.Request参数只有在处理程序返回之前才有效且可以安全使用!这些值(或其中的“部分”)可以重复使用-这是一个实现细节,您也不应假设任何事情。处理程序返回后,您不应触摸(甚至不读取)这些值。

  • 处理程序返回后,将提交响应(或者可以随时提交响应)。这意味着您的新goroutine不应尝试使用http.ResponseWriterafter之后发送回任何数据。即使您没有触摸http.ResponseWriter处理程序中的,这也是正确的,即使没有从处理程序中惊慌也被视为对请求的成功处理,并因此将HTTP 200状态发送回

您可以将http.Requesthttp.ResponseWriter值传递给其他函数和新的goroutine,但必须小心:如果要从多个goroutine中读取/修改这些值,则应使用显式同步(例如,锁,通道)。从多个goroutine发送回数据)。

请注意,看来,如果您的处理程序goroutine和新的goroutine都只读取/检查了http.Request,则可能仍然有问题。是的,多个goroutine可以读取同一变量而无需同步(如果没有人修改它)。但是,调用的某些方法http.Request也会修改http.Request,并且如果没有同步,则无法保证其他goroutine从此更改中会看到什么。例如,Request.FormValue()返回与给定键关联的表单值。但是,此方法调用,ParseMultiPartForm()并且ParseForm()在必要时会对其进行修改http.Request(例如,它们设置Request.PostFormRequest.Form字段)。

所以,除非你同步够程,你不应该传递RequestResponseWriter新够程,但是从需要采集数据Request的处理够程,并通过只如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所做的更改是否需要对他人可见-
但这仍然需要同步,这将由通道解决方案取代)。

2020-07-02