【问题标题】:Golang - Have some troubles with Go-routines and channelsGolang - Go-routines 和 channels 有一些问题
【发布时间】:2018-10-19 16:16:46
【问题描述】:

我对 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。而且对于异步上传来说,上传似乎太慢了。

我知道代码有点乱,抱歉。非常感谢任何帮助!提前致谢!

【问题讨论】:

  • 尽量做最小的例子,看起来你有很多依赖,那么不看实际代码很难找到瓶颈。

标签: go channel


【解决方案1】:

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

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

下一个实现细节是在循环外调用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")

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

【讨论】:

  • 您好,谢谢!我已经相应地更新了我的代码,但是异步操作似乎仍然很慢。您在我的代码中使用过的频道上是否有任何 cmets?我不确定我是否按照他们的意思使用它们。也许这些就是问题
  • 而且我的 POST 请求似乎卡住了:(
  • 它可能很慢,因为实际上传速度很慢。您是否检查过上传单个文件通常需要多长时间?此外,根据网络的带宽和吞吐量,它也可能很慢。请注意,仅仅因为它的并发并不意味着它不受网络或操作系统的运行方式的约束。
  • 我已经添加了新代码。你觉得有什么不对吗?
  • 这就是我要尝试的方法,尝试通过将js.Urls 限制为一个条目来发送 1 个请求而不是 N 个请求,然后查看需要多长时间,然后执行 curl 命令并比较需要多长时间。同时,除非您从当前代码示例中不需要的通道读取数据,否则不需要通道。
【解决方案2】:

您的代码有点混乱。但是,如果我正确理解您要执行的操作,则您正在处理请求列表并希望返回每个请求的 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

也就是说,这有点脆弱。您正在制作与您的 url 列表大小相同的频道。这对于几个 url 可以正常工作,但如果你有一百万个 url 的列表,你将创建一个相当大的频道。您可能希望将通道缓冲区大小固定为某个合理的值,并在发送请求之前检查通道是否已准备好进行处理。这样您就可以避免一次发出一百万个请求。

【讨论】:

  • 哇非常感谢您的回答!这就是我一直在寻找的内容,也感谢您对频道大小的洞察!
  • 不客气。如果你愿意,我可以调整代码来限制并发请求的数量。
  • 那将是非常受欢迎的!我仍在尝试找出语言
  • 这样的。使用另一个通道来控制一次执行的任务数量play.golang.org/p/x3DYitwf6yP
猜你喜欢
  • 1970-01-01
  • 2014-10-11
  • 2018-12-15
  • 2021-12-30
  • 1970-01-01
  • 2018-09-24
  • 2013-12-05
  • 2020-02-02
  • 1970-01-01
相关资源
最近更新 更多