GoLang-与频道的双向通信

问题描述 投票:0回答:2

在下面的代码中:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func getPage(url string) (int, error) {
    resp, err := http.Get(url)
    if err != nil {
        return 0, err
    }

    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return 0, err
    }

    return len(body), nil
}

func getter(urlChan chan string, size chan int) {
    url := <-urlChan
    length, err := getPage(url)
    if err == nil {
        size <- length
        urlChan <- url
    }
}

func main() {
    urls := []string{"http://www.google.com/", "http://www.yahoo.com",
        "http://www.bing.com", "http://bbc.co.uk"}

    sizeChan := make(chan int)
    urlChan := make(chan string)

    for _, url := range urls {
        urlChan <- url
        go getter(urlChan, sizeChan)
    }

    for i := 0; i < len(urls); i++ {
        fmt.Printf("%s has length %d\n", <-urlChan, <-sizeChan)
    }
}

我使用urlChan作为双向,以了解给定大小(urlChan)的url(sizeChan)。

对于getter()例程,在将第一个字符串参数url设置为urlChan后,下面的输出显示挂起情况:

$ go install github.com/shamhub/cs61a
$ 
$ 
$ bin/cs61a 

1)如何在通道上执行双向通信?

2)如何描述给定进程的例行程序状态?

go channel
2个回答
1
投票

您必须阅读和练习Go导览,我相信下面的代码中使用的每个细节都将在此处进行解释,但是,这是一个解决方案

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "sync"
)

func getPage(url string) (int, error) {
    resp, err := http.Get(url)
    if err != nil {
        return 0, err
    }

    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return 0, err
    }

    return len(body), nil
}

type result struct {
    url string
    len int
    err error
}

func main() {
    urls := []string{"http://www.google.com/", "http://www.yahoo.com",
        "http://www.bing.com", "http://bbc.co.uk"}

    resChan := make(chan result)
    var wg sync.WaitGroup
    for _, url := range urls {
        wg.Add(1)
        go func(url string) {
            defer wg.Done()
            l, err := getPage(url)
            resChan <- result{url: url, len: l, err: err}
        }(url)
    }

    go func() {
        wg.Wait()
        close(resChan)
    }()

    for r := range resChan {
        if r.err != nil {
            log.Printf("failed to fetch %q: %v\n", r.url, r.err)
            continue
        }
        fmt.Printf("%s has length %d\n", r.url, r.len)
    }
}

这是不理想的,因为它不会限制并发传出请求的数量。

关于,2) How to profile the status of go-routines for a given process?

请参阅pprof工具。有一个文档https://golang.org/doc/diagnostics.html

https://stackoverflow.com/a/19145992/4466350


0
投票

正如其他人已经指出的,您可以更简单地解决此问题:

type sized struct {
    url    string
    length int
    err    error
}

func sizer(url string, result chan<- sized, wg *sync.WaitGroup) {
    defer wg.Done()
    length, err := getPage(url)
    result <- sized{url, length, err}
}

func main() {
    urls := []string{"http://www.google.com/", "http://www.yahoo.com",
        "http://www.bing.com", "http://bbc.co.uk"}

    ch := make(chan sized)
    var wg sync.WaitGroup
    for _, url := range urls {
        wg.Add(1)
        go sizer(url, ch, &wg)
    }
    go func() {
        wg.Wait()
        close(ch)
    }()

    for result := range ch {
        if result.err != nil {
            fmt.Printf("%s: %s\n", result.url, result.err)
        } else {
            fmt.Printf("%s: length = %d\n", result.url, result.length)
        }
    }
}

Complete example on playground, though it can't dial out, so not really useful there.

如何在通道上进行双向通信?

您不能。

嗯,那不是完全正确的。让我们说相反:您不应

假设我们有一个频道:

var ch chan T

对于某些类型T。

频道本身本质上是...好吧,我认为非定向是正确的词:

  • anyone可以使用ch <- val]将类型T的任何值放入通道
  • anyone
  • 可以使用var <- ch]从通道中取出任何类型T的值

    这显然不是单向的,但我认为称它为[[双向”是一种误导:它具有“将事物放入”一侧和“取出事物”一侧,两者都集中在一个通道实例中,但是通道与任何特定的user

没有关联。您可以沿任一方向将通道值复制到定向instance(请参见What's the point of one-way channels in Go?),但实际上它们都是对基础非定向通道对象的引用。1然后可以传递原始频道或该频道的单向副本,以提供给any用户,或将其中任何一个视为任何人都可以使用的“全局”(宽范围)变量。现在我们添加两个实体,我们分别称为X和Y。如果通道ch

buffered

,则X和Y之一或两者都可以放入项目,然后A和B之一或两者都放入。可以取出物品。项目将按其进入的顺序出现,从而序列化访问;如果频道已满,则尝试放入项目的操作将被阻止;但是项目不专门转移 X Y。特别是:// Y: paused or stopped // do this in X when ch is initially empty: ch <- T{} v := <-ch
这里,X将零值T放入通道,然后将其取出。这不是定向的。 X返回了[[自己的数据。

如果通道不是为空,但也未满,则X或Y都可以向其中添加某些内容(作为队列),然后从队列的最前面删除某些内容。但这只是将频道用作队列。 (请参见Is it possible to use Go's buffered channel as a thread-safe queue?

如果此队列具有某些它肯定缺乏的功能,则您[[可以使用单个队列进行某种双向通信(请参阅Is two way comm possible using a single message queue in C)。但是它没有它们。如果确实需要双向通信,答案很明显:使用两个队列。将一个队列(一个通道)指定为X发送到Y通道,将另一个队列指定为Y发送到X通道。

1可以通过unsafe访问/恢复基础通道,但是除非您确切地知道自己在做什么,否则不要这样做。向您传递了单向实例的某人可能不希望您使用该通道的另一侧。

© www.soinside.com 2019 - 2024. All rights reserved.