Goroutines打破了这个计划

时间:2013-08-02 18:51:20

标签: go goroutine

我真的不知道为什么。

问题在于:我和朋友一起制作了一个网络服务器。我认为在页面加载中使用goroutine是有益的,所以这就是我所做的事情:将loadPage函数称为goroutine。但是,执行此操作时,服务器只会停止工作而不会出现错误。它打印一个空白的白页。问题必须出在功能本身 - 某种方式与goroutine有冲突。

这些是相关功能:

func loadPage(w http.ResponseWriter, path string) {
   s := GetFileContent(path)
   w.Header().Add("Content-Type", getHeader(path))
   w.Header().Add("Content-Length", GetContentLength(path))
   fmt.Fprint(w, s)
}
func GetFileContent(path string) string {
   cont, err := ioutil.ReadFile(path)
   e(err)
   aob := len(cont)
   s := string(cont[:aob])
   return s
}


func GetFileContent(path string) string {
   cont, err := ioutil.ReadFile(path)
   e(err)
   aob := len(cont)
   s := string(cont[:aob])
   return s
}

func getHeader(path string) string {
   images := []string{".jpg", ".jpeg", ".gif", ".png"}
   readable := []string{".htm", ".html", ".php", ".asp", ".js", ".css"}
   if ArrayContainsSuffix(images, path) {
      return "image/jpeg"
   }
   if ArrayContainsSuffix(readable, path) {
      return "text/html"
   }
   return "file/downloadable"
}


func ArrayContainsSuffix(arr []string, c string) bool {
   length := len(arr)
   for i := 0; i < length; i++ {
      s := arr[i]
      if strings.HasSuffix(c, s) {
         return true
      }
   }
return false
}

2 个答案:

答案 0 :(得分:2)

发生这种情况的原因是因为调用“loadPage”的HandlerFunc与请求同步调用。当你在go例程中调用它时,Handler实际上会立即返回,导致响应立即发送。这就是你得到一个空白页面的原因。

您可以在server.go(第1096行)中看到这一点:

serverHandler{c.server}.ServeHTTP(w, w.req)
if c.hijacked() {
    return
}
w.finishRequest()

ServeHTTP函数调用你的处理程序,一旦它返回它就调用“finishRequest”。因此,只要想要满足请求,您的处理程序功能就必须阻止。

使用go例程实际上不会使您的页面更快。正如菲利普所建议的那样,将一个单一的例行程序与一个频道同步,在这种情况下也不会对你有所帮助,因为这与没有完成日常工作是一样的。

问题的根源实际上是ioutil.ReadFile,它会在发送之前将整个文件缓冲到内存中。

如果要传输文件,则需要使用os.Open。您可以使用io.Copy将文件内容流式传输到浏览器,该浏览器将使用分块编码。

这看起来像这样:

f, err := os.Open(path)
if err != nil {
    http.Error(w, "Not Found", http.StatusNotFound)
    return
}
n, err := io.Copy(w, f)
if n == 0 && err != nil {
    http.Error(w, "Error", http.StatusInternalServerError)
    return
}

如果出于某种原因需要在多个例行程序中完成工作,请查看sync.WaitGroup。渠道也可以运作。

如果您尝试仅提供文件,则还有其他针对此优化的选项,例如FileServerServeFile

答案 1 :(得分:0)

在Go中的典型Web框架实现中,路由处理程序被调用为Goroutines。即在某些时候,网络框架会说go loadPage(...)

因此,如果您从内部 loadPage调用Go例程,则您有两个级别的Goroutines。

Go调度程序非常懒,如果没有强制执行,则不会执行第二级。所以你需要通过同步事件来强制执行它。例如。通过使用渠道或sync包。例如:

func loadPage(w http.ResponseWriter, path string) {
  s := make(chan string)
  go GetFileContent(path, s)
  fmt.Fprint(w, <-s)
}

Go documentation说:

  

如果另一个goroutine必须观察到goroutine的影响,   使用锁或通道等同步机制   建立相对排序的沟通。

为什么这真的很聪明?在较大的项目中,您可以处理大量需要以某种方式有效协调的Goroutines。那么,如果无法使用输出,为什么要调用Goroutine呢?一个有趣的事实:像fmt.Printf这样的I / O操作也会触发同步事件。