如何等待一组goroutine中的* any *发出信号,而不要求我们等待他们这样做

时间:2018-02-02 01:37:08

标签: multithreading go producer-consumer goroutine

有很多关于如何使用WaitGroup等待一组goroutine的所有来完成的例子,但是如果你想等待任何一个的话他们完成而不使用信号量系统,其中一些进程必须等待?例如,生产者/消费者场景,其中多个生产者线程将多个条目添加到数据结构,而消费者一次一个地删除它们。在这种情况下:

  • 我们不能只使用标准的生产者/消费者信号量系统,因为生产:消费不是1:1,而且因为数据结构充当缓存,所以生产者可以"自由运行"而不是阻止,直到消费者准备好"消费他们的产品。
  • 消费者可能会清空数据结构,在这种情况下,消费者希望等到任何一个生产者完成(意味着数据结构中可能存在新内容)< / LI>

问题:有没有标准方法可以做到这一点?

我只能设计两种方法。两者都使用通道作为信号量:

var unitary_channel chan int = make(chan int, 1)

func my_goroutine() {
   // Produce, produce, produce!!!
   unitary_channel<-0 // Try to push a value to the channel
   <-unitary_channel // Remove it, in case nobody was waiting
}

func main() {
   go my_goroutine()
   go my_goroutine()
   go my_goroutine()
   for len(stuff_to_consume) { /* Consume, consume, consume */ }
   // Ran out of stuff to consume
   <-unitary_channel
   unitary_channel<-0 // To unblock the goroutine which was exiting
   // Consume more
}

现在,这个简单的例子有一些明显的(但是可以解决的问题),比如如果至少有一个go_routine()仍在运行,main()就不会退出。

第二种方法,不是要求生产者删除它们刚刚推送到通道的值,而是使用 select 来允许生产者在通道阻止它们时退出。

var empty_channel chan int = make(chan int)

func my_goroutine() {
   // Produce, produce, produce!!!
   select {
      case empty_channel <- 0: // Push if you can
      default:  // Or don't if you can't
   }
}

func main() {
   go my_goroutine()
   go my_goroutine()
   go my_goroutine()
   for len(stuff_to_consume) { /* Consume, consume, consume */ }
   // Ran out of stuff to consume
   <-unitary_channel
   // Consume more
}

当然,如果所有goroutine都已经终止,那么这个永远阻止main()。 所以,如果第一个问题的答案是&#34;否,除了你提出的问题之外没有其他标准的解决方案,是否有令人信服的理由?为什么应该使用其中一个而不是另一个?

1 个答案:

答案 0 :(得分:0)

您可以使用带有缓冲区的频道

// create a channel with a buffer of 1
var Items = make(chan int, 1)
var MyArray []int

func main() {
    go addItems()
    go addItems()
    go addItems()
    go sendToChannel()
    for true {
        fmt.Println(<- Items)
    }
}

// push a number to the array
func addItems() {
    for x := 0; x < 10; x++ {
        MyArray = append(MyArray, x)
    }
}

// push to Items and pop the array
func sendToChannel() {
    for true {
        for len(MyArray) > 0 {
            Items <- MyArray[0]
            MyArray = MyArray[1:]
        }
        time.Sleep(10 * time.Second)
    }
}

main中的for循环将循环播放并打印任何添加到通道的内容,sendToChannel函数将在数组为空时阻塞,

通过这种方式,生产者永远不会被阻止,消费者可以在有一个或多个可用项目时消费