当输入数量未知时如何关闭通道?
How to close channel when there is unknown number of inputs to it?
API : https://jsonmock.hackerrank.com/api/articles?page=1
package main
import (
"fmt"
"net/http"
"encoding/json"
"strconv"
"sync"
)
type ArticleResponse struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
Total int `json:"total"`
TotalPages int `json:"total_pages"`
Data []Article `json:"data"`
}
type Article struct {
Title string `json:"title"`
URL string `json:"url"`
Author string `json:"author"`
NumComments int `json:"num_comments"`
StoryID int32 `json:"story_id"`
StoryTitle string `json:"story_title"`
StoryURL string `json:"story_url"`
ParentID int32 `json:"parent_id"`
CreatedAt int `json:"created_at"`
}
type CommentTitle struct{
NumberOfComments int `json:"number_of_comments"`
Title string `json:"title"`
Page int `json:"from_page"`
}
const (
GET_HOST_URL = "https://jsonmock.hackerrank.com/"
PATH = "api/articles"
)
var wg sync.WaitGroup
func main(){
comment_title_chan := make(chan CommentTitle)
var commentTitleSlice []CommentTitle
// pilot call to get total number of pages
totalPage := makePaginatedRequest(1, comment_title_chan, true)
// making concurrent requests to multiple pages at once
for j:=1;j<=totalPage;j++{
go makePaginatedRequest(j, comment_title_chan, false)
}
for j:=0; j<20;j++ {
commentTitleSlice = append(commentTitleSlice, <-comment_title_chan)
}
for _,article := range commentTitleSlice{
fmt.Println(article.NumberOfComments, "\t\t", article.Title)
}
}
func makePaginatedRequest(pageNo int, chunk chan CommentTitle, pilotMode bool) int{
uri := GET_HOST_URL + PATH
req, err := http.NewRequest("GET", uri, nil)
if err != nil {
fmt.Println(err)
}
q := req.URL.Query()
q.Add("page", strconv.Itoa(pageNo))
req.URL.RawQuery = q.Encode()
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error on response.\n[ERROR] -", err)
}
defer resp.Body.Close()
var articleResponse ArticleResponse
if err = json.NewDecoder(resp.Body).Decode(&articleResponse) ; err != nil{
fmt.Println(err)
}
if !pilotMode{
for _, article := range articleResponse.Data{
if(article.Title != "" && article.NumComments != 0){
ct := CommentTitle{article.NumComments, article.Title, pageNo}
wg.Add(1)
chunk <- ct
wg.Done()
}
}
wg.Wait()
}
return articleResponse.TotalPages
}
问题陈述:
有一个 api 在查询参数中传递页码时提供数据。我需要调用所有页面并获取所有具有有效标题和评论数字段的文章。
解决方案:
第 1 步:我首先对 api 进行试点调用以了解页数,因为它是响应 json.
的一部分
第 2 步:我启动了多个 goroutine(goroutine 的数量 = 总页数)
step-3:每个goroutine都会调用相应的page,获取数据发送到data channel。
step-4:channel接收到的数据附加到一个slice,slice用于进一步计算(根据文章的评论数排序)
问题:
我不知道记录总数 - 有多少是有效的,因此我不知道何时关闭来自发送者的通道(在我的场景中是多个发送者和单个接收者)。
我尝试使用一个额外的信号通道,但我什么时候才能知道所有 goroutine 都已完成它们的工作,以便我可以发送信号以进行进一步计算?
我什至使用过 WaitGroup,但这是在单个 goroutine 级别 - 我仍然无法知道所有 goroutine 何时完成了它的工作。
SO 中的另一个类似问题没有多大帮助:
更新:在代码中,我将 j 循环值硬编码为 20 - 这正是我面临问题的地方。我不知道循环到哪里,如果我将其增加到 50 以上,接收将被阻止。
您错误地使用了等待组。
创建goroutine时加入waitgroup,在一个单独的goroutine中等待一切完成,然后关闭通道:
for j:=1;j<=totalPage;j++{
wg.Add(1)
go makePaginatedRequest(j, comment_title_chan, false)
}
go func() {
wg.Wait()
close(comment_title_chan)
}()
当 goroutine returns:
时标记完成
func makePaginatedRequest(pageNo int, chunk chan CommentTitle, pilotMode bool) int{
defer wg.Done()
...
API : https://jsonmock.hackerrank.com/api/articles?page=1
package main
import (
"fmt"
"net/http"
"encoding/json"
"strconv"
"sync"
)
type ArticleResponse struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
Total int `json:"total"`
TotalPages int `json:"total_pages"`
Data []Article `json:"data"`
}
type Article struct {
Title string `json:"title"`
URL string `json:"url"`
Author string `json:"author"`
NumComments int `json:"num_comments"`
StoryID int32 `json:"story_id"`
StoryTitle string `json:"story_title"`
StoryURL string `json:"story_url"`
ParentID int32 `json:"parent_id"`
CreatedAt int `json:"created_at"`
}
type CommentTitle struct{
NumberOfComments int `json:"number_of_comments"`
Title string `json:"title"`
Page int `json:"from_page"`
}
const (
GET_HOST_URL = "https://jsonmock.hackerrank.com/"
PATH = "api/articles"
)
var wg sync.WaitGroup
func main(){
comment_title_chan := make(chan CommentTitle)
var commentTitleSlice []CommentTitle
// pilot call to get total number of pages
totalPage := makePaginatedRequest(1, comment_title_chan, true)
// making concurrent requests to multiple pages at once
for j:=1;j<=totalPage;j++{
go makePaginatedRequest(j, comment_title_chan, false)
}
for j:=0; j<20;j++ {
commentTitleSlice = append(commentTitleSlice, <-comment_title_chan)
}
for _,article := range commentTitleSlice{
fmt.Println(article.NumberOfComments, "\t\t", article.Title)
}
}
func makePaginatedRequest(pageNo int, chunk chan CommentTitle, pilotMode bool) int{
uri := GET_HOST_URL + PATH
req, err := http.NewRequest("GET", uri, nil)
if err != nil {
fmt.Println(err)
}
q := req.URL.Query()
q.Add("page", strconv.Itoa(pageNo))
req.URL.RawQuery = q.Encode()
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error on response.\n[ERROR] -", err)
}
defer resp.Body.Close()
var articleResponse ArticleResponse
if err = json.NewDecoder(resp.Body).Decode(&articleResponse) ; err != nil{
fmt.Println(err)
}
if !pilotMode{
for _, article := range articleResponse.Data{
if(article.Title != "" && article.NumComments != 0){
ct := CommentTitle{article.NumComments, article.Title, pageNo}
wg.Add(1)
chunk <- ct
wg.Done()
}
}
wg.Wait()
}
return articleResponse.TotalPages
}
问题陈述:
有一个 api 在查询参数中传递页码时提供数据。我需要调用所有页面并获取所有具有有效标题和评论数字段的文章。
解决方案:
第 1 步:我首先对 api 进行试点调用以了解页数,因为它是响应 json.
的一部分第 2 步:我启动了多个 goroutine(goroutine 的数量 = 总页数)
step-3:每个goroutine都会调用相应的page,获取数据发送到data channel。
step-4:channel接收到的数据附加到一个slice,slice用于进一步计算(根据文章的评论数排序)
问题: 我不知道记录总数 - 有多少是有效的,因此我不知道何时关闭来自发送者的通道(在我的场景中是多个发送者和单个接收者)。
我尝试使用一个额外的信号通道,但我什么时候才能知道所有 goroutine 都已完成它们的工作,以便我可以发送信号以进行进一步计算?
我什至使用过 WaitGroup,但这是在单个 goroutine 级别 - 我仍然无法知道所有 goroutine 何时完成了它的工作。
SO 中的另一个类似问题没有多大帮助:
更新:在代码中,我将 j 循环值硬编码为 20 - 这正是我面临问题的地方。我不知道循环到哪里,如果我将其增加到 50 以上,接收将被阻止。
您错误地使用了等待组。
创建goroutine时加入waitgroup,在一个单独的goroutine中等待一切完成,然后关闭通道:
for j:=1;j<=totalPage;j++{
wg.Add(1)
go makePaginatedRequest(j, comment_title_chan, false)
}
go func() {
wg.Wait()
close(comment_title_chan)
}()
当 goroutine returns:
时标记完成func makePaginatedRequest(pageNo int, chunk chan CommentTitle, pilotMode bool) int{
defer wg.Done()
...