如何在Go中同步可变数量的通道?

如何在Go中同步可变数量的通道?,go,Go,我通过编写一个客户端(以web服务器的形式)来测试Go中的并发性,该客户端向web服务器发出许多请求,然后返回发出这些请求所需的时间。基本上是一个基准工具 以下是我当前使用的代码: 我知道我的代码有很多缺陷,但我现在关心的是,如果我添加更多的go例程调用,测试性能会发生怎样的变化需要付出多少努力。在CallSync函数中,我必须继续添加这个庞大的select语句和启动go例程的func调用的庞大列表。我知道一定有更好的办法。我甚至不需要像现在这样进行同步,但如果我这样做了,我怎么能以更灵活的方式

我通过编写一个客户端(以web服务器的形式)来测试Go中的并发性,该客户端向web服务器发出许多请求,然后返回发出这些请求所需的时间。基本上是一个基准工具

以下是我当前使用的代码:


我知道我的代码有很多缺陷,但我现在关心的是,如果我添加更多的go例程调用,测试性能会发生怎样的变化需要付出多少努力。在CallSync函数中,我必须继续添加这个庞大的select语句和启动go例程的func调用的庞大列表。我知道一定有更好的办法。我甚至不需要像现在这样进行同步,但如果我这样做了,我怎么能以更灵活的方式进行呢?我想要有代码,我可以指定要调用的“go例程”的数量,它将调用该例程的次数,并与所有相关通道同步,而无需对其全部进行硬编码。

就像@jiangd建议的那样,只使用一个通道更容易:

type resp struct {
    id string
    i  int
}

func Async(url string, c chan<- resp, id string, count int) {
    cnt := 0
    for i := 0; i < count; i++ {
        GetPage(url)
        cnt = cnt + 1
        if cnt == 50 {
            c <- resp{id, i}
            cnt = 0
        }
    }
}

func CallSync(w http.ResponseWriter, r *http.Request) {
    t0 := time.Now()
    ch := make(chan resp, 20)
    for i := range [20]struct{}{} {
        go Async("http://localhost:8080/", ch, fmt.Sprintf("ch%02d", i), 2000)
    }
    count := 0
    for count < 4000 {
        select {
        case r := <-ch:
            fmt.Printf("%+v\n", r)
        default:
        }
    }
    t1 := time.Now()
    diff := t1.Sub(t0)
    num := diff.Nanoseconds() / int64(time.Millisecond)
    msec := float64(num) / 1000
    reqsec := float64(count) / msec
    fmt.Fprintf(w, "%v requests\n", count)
    fmt.Fprintf(w, "Performed in %v\n", diff)
    fmt.Fprintf(w, "At a rate of %v (requests/sec)\n", reqsec)
}
类型响应结构{
id字符串
i int
}

func Async(url字符串,c)为什么不使用一个通道作为所有通道(c1…cN)。您可以在多个go例程中编写一个通道。我知道您说过您知道许多缺陷;但有两个重要的缺陷您可能不知道,我想指出。首先,删除
默认值:
开关的情况;
;它使它成为一个非阻塞繁忙循环,而不是阻塞,直到有东西可以读取。第二,什么时候
http.Get
返回一个错误,如果err!=nil,您需要在
块中输入一个返回值,因为
resp.Body.Close()
将死机,因为
resp
无效。仅供参考,这里有一个“已清除”基于版本:如果出于某种原因,您确实希望保留每个频道的频道,您可以查看一些模式以收集多个频道的结果;特别是,
多路复用
功能可能与您要查找的功能非常接近。在看到make语句中添加了20个之后,我意识到我需要进一步了解channels。我很确定我现在明白了,但我要说的是,你可以从make中删除这个额外的参数,它仍然可以工作,但效率会降低,因为发送方总是要阻塞才能等待发送,对吗?通过创建一个与你启动的go例程数量相同大小的缓冲区,你基本上永远不会有ch发送端的annel块?你能描述一下在“range[20]struct{}{}}{}”中带有range的for循环是怎么说的吗?有没有超过循环20次的内容?@Fyndor正确,是“20”是通道的缓冲区,在没有它的情况下它仍然可以工作,但是调用将阻塞,直到您读取结果为止。如果缓冲区被填满,并且您的读取器处理缓慢(但在您的示例中不应该发生这种情况)并且i:=range[20]struct{}
是一种奇特的方法,它为i:=0;i<20;i++创建一个由20个
struct{}
元素和循环组成的数组,
struct{}
是特殊的,最多使用1字节内存。