Http Webhook进程在另一个goroutine上运行

Http Webhook进程在另一个goroutine上运行,http,go,server,goroutine,Http,Go,Server,Goroutine,我想在另一个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.ResponseWri

我想在另一个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..")
}
foo := int64(0)
bar := func(foo int64) {
   // do slow things with param foo (not the local foo var)
}
go bar(foo)

这样做有什么问题吗?

如果你想确认邮件,那么发布的代码就没有帮助了。在单独的goroutine中运行代码将使其独立,即使由于goroutine函数中的某些错误而未发送邮件,服务器回复也将成功。

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

有些事情需要注意:

  • 新的goroutine独立于处理程序goroutine运行。这意味着它可能在处理程序goroutine之前或之后完成,在没有显式同步的情况下,您不能(不应该)假设与此相关的任何事情

  • 处理程序的和参数仅在处理程序返回之前有效且安全!这些值(或其中的“部分”)可以重用——这是一个实现细节,您也不应该假设任何东西。一旦处理程序返回,您就不应该触摸(甚至不读取)这些值

  • 一旦处理程序返回,响应就被提交(或者可以在任何时刻提交)。这意味着您的新goroutine在此之后不应尝试使用
    http.ResponseWriter
    发回任何数据。这在某种程度上是正确的,即使您不触摸处理程序中的
    http.ResponseWriter
    ,从处理程序中不惊慌也被视为成功处理了请求,因此http 200状态被发回()

允许您将
http.Request
http.ResponseWriter
值传递给其他函数和新的goroutine,但必须注意:如果您打算从多个goroutine读取/修改这些值,则应使用显式同步(例如锁、通道)(或者您希望从多个goroutine发回数据)

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

因此,除非同步您的goroutine,否则您不应该将
请求
响应编写器
传递给新的goroutine,而是从处理程序goroutine中的
请求
中获取所需的数据,并且只传递例如保存所需数据的
结构

你的第二个例子:

foo := int64(0)
bar := func() {
   // do slow things with foo
}
go bar()
这很好,这是一个函数,它所引用的局部变量只要可以访问,就可以继续存在

请注意,也可以将局部变量的值作为参数传递给匿名函数调用,如下所示:

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..")
}
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所做的更改是否需要对另一方可见-但这无论如何都需要同步,而同步将被通道解决方案所取代)