我想在一个频道中发送一个值来从主函数中转到例程。发生的事情是哪个例程将首先从通道接收值。
package main
import (
"fmt"
"math/rand"
//"runtime"
"strconv"
"time"
)
func main() {
var ch chan int
ch = make(chan int)
ch <- 1
receive(ch)
}
func receive(ch chan int){
for i := 0; i < 4; i++ {
// Create some threads
go func(i int) {
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
fmt.Println(<-ch)
}(i)
}
}
我目前的实施是错误的。
致命错误:所有goroutine都睡着了 - 死锁!
我怎么知道哪个例程将首先从频道接收值。其他go例程会发生什么情况如果那些会运行或抛出错误,因为没有通道可以接收该值。因为其中一个人已经收到了。
如果创建一个缓冲通道我的代码可以工作。因此,我不知道在创建如下所示的缓冲通道时,它在场景背后发生了什么:
func main() {
var ch chan int
ch = make(chan int, 10)
ch <- 1
receive(ch)
}
如果我们看下面的代码。我可以看到我们可以直接通过通道发送值,不需要创建一个go例程来通过一个通道向另一个例程发送一个值。
package main
import "fmt"
func main() {
// We'll iterate over 2 values in the `queue` channel.
queue := make(chan string, 2)
queue <- "one"
queue <- "two"
close(queue)
for elem := range queue {
fmt.Println(elem)
}
}
然后我的代码出了什么问题。为什么会造成僵局。
答案 0 :(得分:1)
无缓冲通道(没有长度)阻塞,直到收到该值。这意味着写入通道的程序将在写入通道后停止,直到读取它为止。如果在主线程中发生这种情况,则在调用receive
之前,会导致死锁。
还有两个问题:您需要使用WaitGroup
暂停完成直到完成,并且通道的行为类似于并发队列。特别是,它具有推送和弹出操作,这两个操作都使用<-
执行。例如:
//Push to channel, channel contains 1 unless other things were there
c <- 1
//Pop from channel, channel is empty
x := <-c
这是一个有效的例子:
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func main() {
var ch chan int
ch = make(chan int)
go func() {
ch <- 1
ch <- 1
ch <- 1
ch <- 1
}()
receive(ch)
}
func receive(ch chan int) {
wg := &sync.WaitGroup{}
for i := 0; i < 4; i++ {
// Create some threads
wg.Add(1)
go func(i int) {
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
fmt.Println(<-ch)
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("done waiting")
}
正如您所看到的,WaitGroup
也很简单。您在更高的范围内声明它。它本质上是一个花哨的计数器,有三种主要方法。当您调用wg.Add(1)
时,计数器会增加,当您调用wg.Done()
时,计数器会减少,当您调用wg.Wait()
时,执行将暂停,直到计数器达到0。
答案 1 :(得分:1)
如果只需要启动多个工作人员并将任务发送到任何,那么在向发送值之前,最好先运行工作人员频道,因为正如@mkopriva上面所说,写一个频道是一个阻塞操作。您总是需要有消费者,否则执行将冻结。
func main() {
var ch chan int
ch = make(chan int)
receive(ch)
ch <- 1
}
func receive(ch chan int) {
for i := 0; i < 4; i++ {
// Create some threads
go func(i int) {
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
fmt.Printf("Worker no %d is processing the value %d\n", i, <-ch)
}(i)
}
}
问题的简短回答&#34;哪个常规会收到它?&#34; - 无论。 :)其中任何一个,你都无法肯定地说。
但是我不知道什么是time.Sleep(...),保持原样。