[英]Golang Goroutine synchronization with channel
I have the following program where the HTTP server is created using gorilla mux.我有以下程序,其中使用 gorilla mux 创建 HTTP 服务器。 When any request comes, it starts goroutine 1. In processing, I am starting another goroutine 2. I want to wait for goroutine 2's response in goroutine 1?
当任何请求到来时,它会启动 goroutine 1。在处理过程中,我正在启动另一个 goroutine 2。我想在 goroutine 1 中等待 goroutine 2 的响应? How I can do that?
我怎么能做到这一点? How to ensure that only goroutine 2 will give the response to goroutine 1?
如何确保只有 goroutine 2 会响应 goroutine 1?
There can be GR4 created by GR3 and GR 3 should wait for GR4 only. GR3 可以创建 GR4,GR 3 应该只等待 GR4。
GR = Goroutine GR = 协程
SERVER服务器
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
}
CLIENT客户
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))
}
RESULT ACTUAL结果实际
{"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":""}
RESULT EXPECTED结果预期
{"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":""}
there are few ways to do this , a simple way is to use channels有几种方法可以做到这一点,一个简单的方法是使用渠道
change the getTitle func to this将 getTitle 函数更改为此
func getTitle(resultCh chan string) {
time.Sleep(2 * time.Second)
resultCh <- "Game Of Thrones"
}
and the getPosts will use it like this 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)
}
i suspect you are new to go, this is a basic channel usage , check more details here Channels我怀疑您是新手,这是一个基本的频道用法,请在此处查看更多详细信息频道
So the problem you're having is that you haven't really leaned how to deal with concurrent code (not a dis, I was there once).所以你遇到的问题是你还没有真正倾向于如何处理并发代码(不是dis,我曾经在那里)。 Most of this centers not around channels.
其中大部分都不是围绕渠道。 The channels are working correctly as @kojan's answer explains.
正如@kojan 的回答所解释的那样,频道工作正常。 Where things go awry is with the
i
variable.事情出错的地方是
i
变量。 Firstly you have to understand that i
is not being mutated atomically so if your client requests arrive in parallel you can mess up the number:首先,您必须了解
i
没有被原子地变异,因此如果您的客户端请求并行到达,您可能会弄乱数字:
C1 : C2:
i == 6 i == 6
i++ i++
i == 7 i == 7
Two increments in software become one increment in actuality because i++
is really 3 operations: load, increment, store.软件中的两个增量实际上变成了一个增量,因为
i++
实际上是 3 个操作:加载、增量、存储。
The second problem you have is that i
is not a pointer, so when you pass i
to your go routine you're making a copy.你遇到的第二个问题是
i
不是一个指针,所以当你将i
传递给你的 go 例程时,你正在制作一个副本。 the i
in the go routine is sent back on the channel and becomes the first number in your concatenated string which you can watch increment. go 例程中的
i
被发送回通道,并成为您可以观看增量的连接字符串中的第一个数字。 However the i
left behind which is used in the tail of the string has continued to be incremented by successive client invocations.然而,在字符串尾部使用的
i
继续通过连续的客户端调用而增加。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.