我正在尝试使用通道在go例程之间来回传递数据。在执行过程中的某个时候,过程冻结了。我无法通过Delve确定何时或为什么。我在这到底是怎么了?
func handler(w http.ResponseWriter, r *http.Request) {
records := liaison.Parse(r) // Parse incoming JSON into a slice.
nRecords := len(records)
in, out := make(chan map[string]string), make(chan map[string]string)
// Start a go routine for each CPU core.
for i := 0; i < nCores; i++ {
go clean(in, out)
}
// Pass each map in the slice to the input channel.
for _, record := range records {
in <- record
}
// Replace each index of the records slice with cleaned data.
for i := 0; i < nRecords; i++ {
cleaned := <-out
records[i] = cleaned
}
close(in)
close(out)
liaison.Respond(w, &records)
}
func clean(in, out chan map[string]string) {
record := <-in
// run cleaning rules on input record.
out <- record
}
答案 0 :(得分:4)
程序使用无缓冲通道。在发送方和接收方都准备就绪之前,在无缓冲通道上的通信不会成功。
如果nRecords
大于nCores
,则程序将死锁:清洁程序goroutine无法发送到out
,直到处理程序goroutine从out
接收到。处理程序goroutine无法接收,因为它阻止发送到in
。
如果nRecords
小于nCores
,则没有工作的清洁工会在发送到out
时惊慌失措。当从处理程序goroutine调用close(in)
时,未完成工作的清理程序将收到<-in
的零值。清洁程序goroutine将处理该零值并尝试发送到out
。同时,处理程序goroutine关闭out
。应用程序出现紧急情况,因为不允许在封闭的通道上发送。
这里是解决方法:
func handler(w http.ResponseWriter, r *http.Request) {
records := liaison.Parse(r) // Parse incoming JSON into a slice.
nRecords := len(records)
in, out := make(chan map[string]string), make(chan map[string]string)
// Start a go routine for each CPU core.
for i := 0; i < nCores; i++ {
go clean(in, out)
}
// Start a goroutine to feed the data. This this allows
// the handler goroutine to continue to receiving on out.
go func() {
for _, record := range records {
in <- record
}
// Close the channel. This causes the cleaners to exit
// from the for / range loops.
close(in)
}()
// Replace each index of the records slice with cleaned data.
for i := 0; i < nRecords; i++ {
cleaned := <-out
records[i] = cleaned
}
liaison.Respond(w, &records)
}
func clean(in, out chan map[string]string) {
// use for / range to read records until in is closed.
for record := range in {
// run cleaning rules on input record.
out <- record
}
}
另一种方法是用于将in
更改为具有所有记录空间的缓冲通道:
func handler(w http.ResponseWriter, r *http.Request) {
records := liaison.Parse(r) // Parse incoming JSON into a slice.
nRecords := len(records)
in := make(chan map[string]string, nRecords) // <-- note second argument
out := make(chan map[string]string)
// Start a go routine for each CPU core.
for i := 0; i < nCores; i++ {
go clean(in, out)
}
for _, record := range records {
// this will not block because cap(in) == len(records).
in <- record
}
close(in)
// Replace each index of the records slice with cleaned data.
for i := 0; i < nRecords; i++ {
cleaned := <-out
records[i] = cleaned
}
liaison.Respond(w, &records)
}