Двунаправленная связь с каналом - PullRequest
0 голосов
/ 08 мая 2020

В приведенном ниже коде:

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 как двунаправленный, чтобы узнать URL-адрес (urlChan) для заданного размера (sizeChan).

Для getter() go -программы, после преобразования первого строкового аргумента от url до urlChan, в выводе ниже показано зависание:

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

1) Как осуществлять двунаправленную связь по каналу?

2) Как профилировать статус go -программ для данного процесса?

Ответы [ 2 ]

3 голосов
/ 08 мая 2020

Как уже отмечали другие, вы можете решить эту проблему намного проще:

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)
        }
    }
}

Полный пример на игровой площадке, хотя он не может дозвониться, поэтому там не очень полезно.

Как осуществлять двунаправленную связь по каналу?

Нельзя.

Ну, это не совсем так. Скажем вместо этого: вы не должны .

Предположим, у нас есть канал:

var ch chan T

для некоторого типа T.

The канал по своей сути ... ну, я думаю, ненаправленный - правильное слово:

  • любой может поставить любое значение введите T в канал с помощью ch <- val
  • любой может взять любое значение типа T из канала с помощью var <- ch

Это явно не t однонаправленный, но я думаю, что называть его двунаправленным вводит в заблуждение: у него есть стороны «положить вещи в» и «убрать вещи», обе сгруппированы в один экземпляр канала, но канал не связан с любым конкретным пользователем . Вы можете скопировать значение канала в направленный экземпляр (см. В чем смысл односторонних каналов в Go? ) в любом направлении, но на самом деле оба являются просто ссылками на базовый объект ненаправленного канала. 1 Затем вы можете передать исходный канал или его однонаправленную копию любому пользователю или рассматривать любой из них как «глобальный» "(широкие) переменные, которые может использовать каждый.

Теперь давайте добавим две сущности, которые мы назовем X и Y. Если канал ch имеет буферизованный , один или оба из 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 могут что-то добавить к нему ( как очередь), затем снимите что-нибудь с начала очереди. Но это просто использование канала как очереди. (См. Можно ли использовать буферизованный канал Go в качестве потокобезопасной очереди? )

Если бы эта очередь имела определенные функции, которых ей определенно не хватает, вы могли бы использовать единственная очередь для своего рода двунаправленной связи (см. Возможна ли двусторонняя связь с использованием единственной очереди сообщений в C). Но их там нет. Если вам действительно нужна двунаправленная связь, ответ очевиден: используйте две очереди. Назначьте одну очередь - один канал - как канал X-отправляет-на-Y, а другой - как канал Y-отправляет-к-X.


1 Базовый канал доступен / восстанавливается через unsafe, но не делайте этого, если вы действительно не знаете, что делаете. Кто-то, кто передал вам однонаправленный экземпляр, вероятно, не намерен, чтобы вы использовали другую «сторону» канала.

1 голос
/ 08 мая 2020

вы должны прочитать и попрактиковаться в 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)
    }
}

Это не идеально, потому что оно будет не ограничивать количество одновременных исходящих запросов.

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

См. инструменты pprof. Есть до c https://golang.org/doc/diagnostics.html

и { ссылка }

...