Golang-Go例程和频道有一些麻烦

时间:2018-10-19 16:16:46

标签: go channel

我是Golang的新手,正在尝试开发一个将图像异步上传到imgur的程序。但是我的代码遇到了一些困难。

这是我的任务;

func uploadT(url string,c chan string, d chan string)  {

    var subtask string
    subtask=upload(url)

    var status string
    var url string

    if subtask!=""{
        status = "Success!"
        url =subtask

    } else {
        status = "Failed!"
        url =subtask
    }

    c<-url
    d<-status
}

这是我用于异步上传的POST请求循环;

c:=make(chan string, len(js.Urls))
d:=make(chan string, len(js.Urls))

wg:=sync.WaitGroup{}
for i := range js.Urls{
    wg.Add(1)
    go uploadTask(js.Urls[i],c,d)
    //Below commented out code is slowing down the routine therefore, I commented out.
    //Needs to be working as well, however, it can work if I put this on task as well. I think I'm kinda confused with this one as well
    //pol=append(pol,retro{Url:<-c,Status:<-d})
}
<-c
<-d
wg.Done()
FinishedTime := time.Now().UTC().Format(time.RFC3339)
qwe=append(qwe,outputURLs{
               jobID:jobID,
               retro:pol,
               CreateTime: CreateTime,
               FinishedTime: FinishedTime,
           })
fmt.Println(jobID)

所以我认为我的频道和常规行不通。确实会在上传任务之前打印出jobID。而且对于异步上传来说,上传似乎也太慢了。

我知道代码有点混乱,对此感到抱歉。任何帮助深表感谢!预先感谢!

2 个答案:

答案 0 :(得分:1)

您实际上没有正确使用WaitGroup。每次调用wg.Done()时,它实际上都会从前一个wg.Add中减去1来确定给定的任务已完成。最后,您需要一个wg.Wait()来同步等待所有任务。 WaitGroups通常用于并行运行多个任务。

基于代码示例的最简单方法是将wg传递到任务中,uploadT并在任务内部调用wg.Done()。请注意,您还需要使用指针而不是struct值。

下一个实现的详细信息是在循环外部调用wg.Wait(),因为您要阻塞直到所有任务都完成,因为所有任务都使用go运行,这使其异步。如果您不wg.Wait(),它将像您所说的那样立即记录jobID。让我知道是否清楚。

作为样板,它应该看起来像这样

func task(wg *sync.WaitGroup) {
    wg.Done()
}

wg := &sync.WaitGroup{}
for i := 0; i < 10; i++ {
    wg.Add(1)
    go task(wg)
}

wg.Wait()
// do something after the task is done
fmt.Println("done")

我要注意的另一件事是,在当前的代码示例中,您正在使用通道,但是对要推送到通道中的值没有做任何事情,因此可以从技术上删除它们。

答案 1 :(得分:0)

您的代码有点令人困惑。但是,如果我正确理解您要执行的操作,则您正在处理请求列表,并希望返回每个请求的url和状态以及每个请求完成的时间。您想并行处理它们。

您根本不需要使用WaitGroups。当您只想运行一堆任务而不用担心结果时,只想知道何时完成所有工作时,WaitGroups很好。但是,如果您要返回结果,则渠道就足够了。

这是一个示例代码,可以完成我认为您要尝试的操作

package main

import (
    "time"
    "fmt"
)

type Result struct {
    URL      string
    Status   string
    Finished string
}

func task(url string, c chan string, d chan string) {
    time.Sleep(time.Second)
    c <- url
    d <- "Success"
}

func main() {
    var results []Result
    urls := []string{"url1", "url2", "url3", "url4", "url5"}
    c := make(chan string, len(urls))
    d := make(chan string, len(urls))
    for _, u := range urls {
        go task(u, c, d)
    }
    for i := 0; i < len(urls); i++ {
        res := Result{}
        res.URL = <-c
        res.Status = <-d
        res.Finished = time.Now().UTC().Format(time.RFC3339)
        results = append(results, res)
    }
    fmt.Println(results)
}

您可以在操场上https://play.golang.org/p/N3oeA7MyZ8L

尝试一下

也就是说,这有点脆弱。您正在制作与网址列表相同大小的频道。这对于某些网址会很好,但是如果您有一百万个网址的列表,那么您将获得一个相当大的渠道。您可能需要将通道缓冲区的大小固定为某个合理的值,并在发送请求之前检查通道是否已准备好进行处理。这样,您可以避免一次发出一百万个请求。