去例行公事

时间:2018-07-16 11:05:20

标签: go goroutine

我要走了,所以这可能是一个愚蠢的问题。

我似乎无法弄清楚为什么我的一个go例程被另一个例程阻塞了。我的理解(可能是错误的)go例程作为独立的轻量级线程运行,因此除非我搞砸了,否则它们不应互相阻塞:)

我粘贴了以下代码,希望能帮助您解决此问题。

package main
import "fmt"
import "time"
import "sync"



func worker( jobs <-chan int, job2 chan<- int) {
    defer wg.Done()
    for j := range jobs {
        fmt.Println("finished job", j)
        time.Sleep(time.Second/2)

        if(j%3==0){
           job2 <- j   
        }  

   } 
   close(job2)
   fmt.Println("channel job2 closed")
 }

func worker2(job2 <-chan int) {
    defer wg.Done()
    for i:= range job2 {
        fmt.Println(i)
        time.Sleep(time.Second*10)
    } 
}

var wg sync.WaitGroup

func main() {

    wg.Add(2)
    jobs := make(chan int)

    job2 := make(chan int)

    go func() {
        for j := 1; j <= 10; j++ {
             jobs <- j
        }
        close(jobs)
        fmt.Println("channel jobs closed")
    }()

    go worker(jobs,job2)
    go worker2(job2)

    wg.Wait()
    fmt.Println("exiting main")     

}

运行此代码时,我得到以下输出

finished job 1
finished job 2
finished job 3
finished job 4
3
finished job 5
finished job 6
6
finished job 7
finished job 8
finished job 9
9
finished job 10
channel jobs closed
channel job2 closed
exiting main

但是我期待这样的事情吗?

finished job 1
finished job 2
finished job 3
finished job 4
3
finished job 5
finished job 6
finished job 7
finished job 8
finished job 9
finished job 10
channel jobs closed
6
9    
channel job2 closed
exiting main

1 个答案:

答案 0 :(得分:3)

您的例程属于某种阻塞,因为未缓冲通道。在无缓冲通道上进行写/读是一项阻塞操作。因此,根据定义,您的例程必须彼此等待。

从本质上讲,您半秒钟的睡眠是无关紧要的,因为第二个工人睡眠10秒钟。那10秒将阻止对第二个通道的读取/写入。向该通道添加缓冲区以解决此问题。

我想指出的其他一些事情是:

  • time.Sleep(time.Second/2)无法正常工作(嗯,可以,但是例如除以3则无效)。 time.Sleep需要一个time.Duration参数,即int64。您需要传递类似time.Millisecond * 500的内容
  • 将通道传递给例程,并从没有创建通道的例程中关闭它是一种不好的形式。通道的创建和关闭应包含在一个例程中。如果没有,它会起作用,但是维护将成为一场噩梦。
  • 将您的导入分组,而不是重复import "package",只需使用import ( "package1"\n"package2")
  • 如果不需要,请不要使用全局变量。在启动所有例程的函数中创建等待组,并将指向它的指针传递给所有例程。为了安全起见,包括匿名功能在内(例如,一旦开始向通道添加缓冲区)
  • 考虑研究context.Contextselect构造。您可以创建一个context.WithCancel,然后在所有例程中选择监听ctx.Done()。然后,您可以一口气取消所有例程,而不必处理信号并将内容推送到取消通道上

演示

我已经做了几件事(主要是创建频道,以及一些次要的代码清理),并创建了一个游乐场示例here

相关问题