【问题标题】:Golang Goroutine synchronization with channelGolang Goroutine 与 channel 同步
【发布时间】:2020-03-15 10:35:47
【问题描述】:

我有以下程序,其中使用 gorilla mux 创建 HTTP 服务器。 当任何请求到来时,它会启动 goroutine 1。在处理过程中,我正在启动另一个 goroutine 2。 我想在 goroutine 1 中等待 goroutine 2 的响应?我怎么能这样做? 如何保证只有 goroutine 2 才会响应 goroutine 1?

GR3 可以创建 GR4,GR 3 应该只等待 GR4。

GR = Goroutine

服务器

    package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strconv"
    "time"

    "github.com/gorilla/mux"
)

type Post struct {
    ID    string `json:"id"`
    Title string `json:"title"`
    Body  string `json:"body"`
}

var posts []Post

var i = 0

func getPosts(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    i++
    fmt.Println(i)
    ch := make(chan int)
    go getTitle(ch, i)

    p := Post{
        ID: "123",
    }
    // Wait for getTitle result and update variable P with title

    s := <-ch
    //

    p.Title = strconv.Itoa(s) + strconv.Itoa(i)
    json.NewEncoder(w).Encode(p)
}

func main() {

    router := mux.NewRouter()
    posts = append(posts, Post{ID: "1", Title: "My first post", Body: "This is the content of my first post"})
    router.HandleFunc("/posts", getPosts).Methods("GET")
    http.ListenAndServe(":9999", router)
}

func getTitle(resultCh chan int, m int) {
    time.Sleep(2 * time.Second)
    resultCh <- m
}

客户

package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
    "time"
)


func main(){

for i :=0;i <100 ;i++ {

go main2()

}

    time.Sleep(200 * time.Second)

}

func main2() {

  url := "http://localhost:9999/posts"
  method := "GET"

  client := &http.Client {
  }
  req, err := http.NewRequest(method, url, nil)

  if err != nil {
    fmt.Println(err)
  }
  res, err := client.Do(req)
  defer res.Body.Close()
  body, err := ioutil.ReadAll(res.Body)

  fmt.Println(string(body))
}

实际结果

{"id":"123","title":"25115","body":""}

{"id":"123","title":"23115","body":""}

{"id":"123","title":"31115","body":""}

{"id":"123","title":"44115","body":""}

{"id":"123","title":"105115","body":""}

{"id":"123","title":"109115","body":""}

{"id":"123","title":"103115","body":""}

{"id":"123","title":"115115","body":""}

{"id":"123","title":"115115","body":""}

{"id":"123","title":"115115","body":""}

预期结果

 {"id":"123","title":"112112","body":""}
 {"id":"123","title":"113113","body":""}
 {"id":"123","title":"115115","body":""}
 {"id":"123","title":"116116","body":""}
 {"id":"123","title":"117117","body":""}

【问题讨论】:

    标签: go


    【解决方案1】:

    有几种方法可以做到这一点,一个简单的方法是使用渠道

    把getTitle函数改成这个

    func getTitle(resultCh chan string)  {
       time.Sleep(2 * time.Second)
       resultCh <- "Game Of Thrones"
    }
    

    getPosts 会像这样使用它

    func getPosts(w http.ResponseWriter, r *http.Request) {
       w.Header().Set("Content-Type", "application/json")
    
       ch := make(chan string)
       go getTitle(ch)
    
    
       s := <-ch // this will wait until getTile inserts data to channel 
       p := Post{
           ID: s,
       }
    
       json.NewEncoder(w).Encode(p)
    }
    

    我怀疑你是新来的,这是一个基本的频道使用,在这里查看更多详细信息Channels

    【讨论】:

    • 感谢 Kojan,我创建了频道,但结果不同步。有时我会在 GR1 中得到 GR4 结果。您可以检查服务器和客户端代码。我已经更新了问题
    • 服务器代码看起来是正确的(客户端代码是不敬的)
    • 您可以看到预期与实际的结果
    • 如果你一个一个地触发 curl localhost:9999/posts 那么它可以工作,但是当使用客户端触发多个请求时它会失败
    • 这是不正确的最大值,当 i 被传递给 GR2 时,我们再次调用了 get 函数,并且执行了另一个 i++,所以当 GR2 完成时,我现在是 2,而不是像我们传递给它的 1。这是因为你的 i 是全球性的
    【解决方案2】:

    所以你遇到的问题是你还没有真正了解如何处理并发代码(不是dis,我曾经在那里)。其中大部分都不是围绕渠道进行的。正如@kojan 的回答所解释的那样,这些频道工作正常。出问题的地方是i 变量。首先,您必须了解 i 不会被原子突变,因此如果您的客户端请求并行到达,您可能会弄乱数字:

    C1 :          C2:
    i == 6        i == 6
    i++           i++
    i == 7        i == 7
    

    软件中的两个增量实际上变成了一个增量,因为i++ 实际上是 3 个操作:加载、增量、存储。

    您遇到的第二个问题是 i 不是指针,因此当您将 i 传递给您的 go 例程时,您正在制作副本。 go 例程中的i 被发送回通道,并成为连接字符串中的第一个数字,您可以观看增量。然而,在字符串尾部使用的i 会随着连续的客户端调用而继续递增。

    【讨论】:

    • 谢谢,我没有使用变量 i,而是使用 newvalue := rand.Intn(100) ,,go getTitle(ch, newvalue) .. 现在一切正常
    猜你喜欢
    • 1970-01-01
    • 2013-05-21
    • 2021-10-07
    • 2015-12-07
    • 2022-01-16
    • 2020-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多