我目前正在研究一个学习Golang的个人项目,但是当我开始使用Goroutines时遇到了一些问题,我认为有些概念我并没有正确理解。简而言之,我的代码具有一个带有接收器函数“循环”的结构,该结构在无限循环中每10秒执行一次处理,直到程序终止,该程序已经起作用。现在,我想添加一个简单的Web服务器,在循环运行时,我需要从中更新结构中的某些数据,这是我用来测试该案例的问题的抽象:

package main

import (
    "fmt"
    "net/http"

    "../thing"
)

var Thing1 thing.Thing

func main() {
    Thing1 := thing.NewThing("thing1")
    //This is the loop that is always running
    go Thing1.Loop()
    http.HandleFunc("/", HelloServer)
    http.ListenAndServe(":8080", nil)
}

func HelloServer(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        http.Error(w, "404 Not found", http.StatusNotFound)
        return
    }
    switch r.Method {
    case "GET":
        http.ServeFile(w, r, "index.html")
    case "POST":
        if err := r.ParseForm(); err != nil {
            fmt.Fprintf(w, "There was an error with the form: %v", err)
            return
        }
        fmt.Println("In handler: " + r.FormValue("fData"))
        Thing1.UpdateData(r.FormValue("fData"))
        http.ServeFile(w, r, "index.html")
    default:
        http.Error(w, "405 Method not allowed", http.StatusMethodNotAllowed)
    }

}
package thing

import (
    "fmt"
    "time"
)

type Thing struct {
    ID   string
    data string
}

func NewThing(id string) Thing {
    return Thing{ID: id}
}

func (t *Thing) Loop() {
    for {
        now := time.Now().Format("15:04:05")
        fmt.Println(t.ID + " starts at " + now)
        fmt.Println(t.ID + " Data: " + t.data)
        time.Sleep(time.Second * 10)
    }
}

func (t *Thing) UpdateData(g string) {
    t.data = g
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Test</title>
</head>
<body>
    <p>Test control panel</p>
    <form method="POST" action="/">
        <label>Data</label><input type="text" name="fData" value="">
        <input type="submit" value="submit">
    </form>
</body>
</html>

基本上,当我按下提交按钮时,我想更新struct Things1中的“data”变量,当在循环内打印data变量时,这应该反映出来,但是它不起作用,data变量总是打印为空的。我不知道自己在做什么错,但是我想我缺少关于goroutine的一些基本概念。
任何建议将不胜感激。

最佳答案

main中,您首先开始:

Thing1 := thing.NewThing("thing1")

这将定义一个名为Thing1的新局部变量,就像您编写的一样:
var Thing1 Thing

然后将变量设置为thing.NewThing的结果:
Thing1 = thing.NewThing("thing1")

同时,您的函数HelloServer并未定义任何名为Thing1的新变量,而是使用了外部(包)作用域Thing1 —一个不同的结构实例,ID = ""(空字符串)和data = ""。调用Thing1.UpdateData(r.FormValue("fData"))时,会将空Thing1的地址传递给UpdateData接收器函数。这个未命名的Thing1对象会更新其数据,而您的循环使用ID为Thing1"thing1"

修复起来很容易:只是不要在main中创建新的Thing1。但是,作为JimB noted in a comment,您也不会同步数据更新和数据访问。如果要通过共享进行通信,则需要某种锁定。

(Go encourages sharing by communicating, rather than communicating by sharing.但是,这需要不同的思维方式。)

10-06 14:21