如何关闭频道

时间:2015-08-07 14:31:12

标签: go synchronization queue

我尝试改编这个例子: https://gobyexample.com/worker-pools

但我不知道如何停止频道,因为节目不会在频道循环结束时退出。

您能解释一下如何退出该计划吗?

package main

import (
    "github.com/SlyMarbo/rss"
    "bufio"
    "fmt"
    "log"
    "os"
)

func readLines(path string) ([]string, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()

    var lines []string
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }
    return lines, scanner.Err()
}


func worker(id int, jobs <-chan string, results chan<- string) {
    for url := range jobs {
        fmt.Println("worker", id, "processing job", url)
        feed, err := rss.Fetch(url)
        if err != nil {
            fmt.Println("Error on: ", url)
            continue
        }
        borne := 0
        for _, value := range feed.Items {
            if borne < 5 {
                results <- value.Link
                borne = borne +1
            } else {
                continue
            }
        }
    }
}


func main() {
    jobs := make(chan string)
    results := make(chan string)

    for w := 1; w <= 16; w++ {
        go worker(w, jobs, results)
    }


    urls, err := readLines("flux.txt")
    if err != nil { 
        log.Fatalf("readLines: %s", err) 
    }

    for _, url := range urls {
        jobs <- url
    }

    close(jobs)

    // it seems program runs over...
    for msg := range results {
        fmt.Println(msg)
    }
}

flux.txt是一个平面文本文件,如:

1 个答案:

答案 0 :(得分:2)

问题在于,在您所指的示例中,工作池从results读取了9次:

for a := 1; a <= 9; a++ {
    <-results
}

另一方面,你的程序在results上进行了一个范围循环,它具有不同的语义。范围运算符在通道关闭之前不会停止。

for msg := range results {
    fmt.Println(msg)
}

要解决您的问题,您需要关闭results频道。但是,如果您只是在for循环之前调用close(results),那么您很可能会这样做 恐慌,因为工人可能正在写results

要解决此问题,您需要添加另一个频道,以便在完成所有工作时收到通知。您可以使用sync.WaitGroup或:

执行此操作
const (
    workers = 16
)

func main() {
    jobs := make(chan string, 100)
    results := make(chan string, 100)
    var wg sync.WaitGroup

    for w := 0; w < workers; w++ {
        go func() {
            wg.Add(1)
            defer wg.Done()
            worker(w, jobs, results)
        }()
    }

    urls, err := readLines("flux.txt")
    if err != nil {
        log.Fatalf("readLines: %s", err)
    }

    for _, url := range urls {
        jobs <- url
    }

    close(jobs)

    wg.Wait()

    close(results)

    // it seems program runs over...
    for msg := range results {
        fmt.Println(msg)
    }
}

done频道:

package main

import (
    "bufio"
    "fmt"
    "github.com/SlyMarbo/rss"
    "log"
    "os"
)

func readLines(path string) ([]string, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()

    var lines []string
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }
    return lines, scanner.Err()
}

func worker(id int, jobs <-chan string, results chan<- string, done chan struct{}) {
    for url := range jobs {
        fmt.Println("worker", id, "processing job", url)
        feed, err := rss.Fetch(url)
        if err != nil {
            fmt.Println("Error on: ", url)
            continue
        }
        borne := 0
        for _, value := range feed.Items {
            if borne < 5 {
                results <- value.Link
                borne = borne + 1
            } else {
                continue
            }
        }
    }
    close(done)
}

const (
    workers = 16
)

func main() {
    jobs := make(chan string, 100)
    results := make(chan string, 100)
    dones := make([]chan struct{}, workers)

    for w := 0; w < workers; w++ {
        dones[w] = make(chan struct{})
        go worker(w, jobs, results, dones[w])
    }

    urls, err := readLines("flux.txt")
    if err != nil {
        log.Fatalf("readLines: %s", err)
    }

    for _, url := range urls {
        jobs <- url
    }


    close(jobs)

    for _, done := range dones {
        <-done
    }

    close(results)

    // it seems program runs over...
    for msg := range results {
        fmt.Println(msg)
    }
}