Golang синхронизация Goroutine с каналом - PullRequest
3 голосов
/ 15 марта 2020

У меня есть следующая программа, где HTTP-сервер создается с помощью Gorilla Mux. Когда приходит какой-либо запрос, он запускает процедуру 1. При обработке я запускаю другую программу 2. Я хочу дождаться ответа программы 2 в программе 1? Как я могу это сделать? Как гарантировать, что только goroutine 2 даст ответ на goroutine 1?

Может быть GR4, созданный GR3, и GR 3 должна ждать только GR4.

GR = Goroutine

enter image description here

СЕРВЕР

    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":""}

Ответы [ 2 ]

3 голосов
/ 15 марта 2020

Итак, проблема, с которой вы столкнулись, заключается в том, что вы не совсем понимаете, как обращаться с параллельным кодом (не дис, я был там однажды). Большая часть этих центров не вокруг каналов. Каналы работают правильно, как объясняет ответ @ kojan. Где вещи go наперекосяк с переменной i. Во-первых, вы должны понимать, что i не мутируется атомарно, поэтому, если ваши клиентские запросы поступают параллельно, вы можете запутать число:

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

Два шага в программном обеспечении становятся одним шагом в действительности, потому что i++ на самом деле это 3 операции: загрузка, приращение, сохранение.

Вторая проблема, с которой вы столкнулись, заключается в том, что i не является указателем, поэтому, когда вы передаете i в свою подпрограмму go, которую вы делаете копия. i в подпрограмме go отправляется обратно на канал и становится первым числом в вашей объединенной строке, которое вы можете наблюдать с приращением. Однако i оставленный позади, который используется в хвосте строки, продолжал увеличиваться последовательными клиентскими вызовами.

3 голосов
/ 15 марта 2020

есть несколько способов сделать это, простой способ - использовать каналы

изменить getTitle fun c на это

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)
}

Я подозреваю, что вы новичок в go, это базовое c использование канала, подробнее см. Здесь Каналы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...