【问题标题】:golang async buffered channel hangsgolang 异步缓冲通道挂起
【发布时间】:2019-10-26 05:49:45
【问题描述】:

作为第一个项目,我决定编写一个简单的异步 web-scaper。我的想法是有一个任务队列和一个“解决”任务的工人池。在编写程序时遇到了问题。

以下代码挂起:

package main

import (
    "fmt"
    "net/http"
    "time"
)

type Scraper struct {
    client http.Client
    timeout int
    tasks chan string
    results chan int
    ntasks int
}

func (s Scraper) Init(timeout int, workers int) {
    s.client = http.Client{
        Timeout: time.Second * time.Duration(timeout),
    }
    s.timeout = timeout
    s.ntasks = 0
    s.Dispatch(workers)
}

func (s Scraper) Wait() {
    for i := 0; i < s.ntasks; i++ {
        <-s.results
    }
}

func (s Scraper) Task(task string) {
    s.tasks <- task // hangs on this line
    s.ntasks++;
}

func (s Scraper) Dispatch(workers int) {
    s.tasks   = make(chan string, 100)
    s.results = make(chan int,    100)
    for i := 0; i < workers; i++ {
        go s.worker(i)
    }
}

func (s Scraper) worker(id int) {
    for task := range <-s.tasks {
        fmt.Println(task)
        s.results <- 0
    }
}

func main() {

    s := Scraper{}
    s.Init(10, 5)
    s.Task("Hello World")
    s.Wait()

}

虽然不是这样:

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "started  job", j)
        time.Sleep(time.Second)
        fmt.Println("worker", id, "finished job", j)
        results <- j * 2
    }
}

func main() {
    jobs    := make(chan int, 100)
    results := make(chan int, 100)

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

    for j := 1; j <= 5; j++ {
        jobs <- j
    }

    close(jobs)

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

}

查看堆栈溢出,我看到无缓冲通道挂起,但 make(chan string, 100) 应该创建一个缓冲通道。

【问题讨论】:

  • 所有方法都使用值而不是指针接收器。因此,这些方法都不会对值产生任何影响 - 因为它们会更改结构的副本 - 而不是原始结构值。请参阅tour 进行复习。
  • 你也在worker中的字符串而不是你的频道。
  • 而ntasks 需要一个锁才能保证 goroutine 安全。

标签: go


【解决方案1】:

将所有接收器更改为如下指针:

func (s *Scraper) Init(timeout int, workers int) // *Scraper not 'Scraper'

有关指针接收器的更多详细信息:https://tour.golang.org/methods/4

正如@JimB 所说 - 该范围也有一个错误,应该是这样的:

func (s *Scraper) worker(id int) {
    // `range s.tasks` not `range <-s.tasks`
    for task := range s.tasks {
        fmt.Println(task)
        s.results <- 0
    }
}

带有接收器和范围修复的游乐场:https://play.golang.org/p/RulKHHfnvJo

【讨论】:

    猜你喜欢
    • 2016-08-30
    • 1970-01-01
    • 2020-09-03
    • 2014-09-26
    • 2016-10-08
    • 2018-07-03
    • 2016-03-18
    • 2017-07-22
    • 2014-01-30
    相关资源
    最近更新 更多