在下面的示例中,go例程将值泵入未缓冲的通道,并且主要功能对其进行迭代。
import React, { useEffect, useState } from 'react';
import { Select, Input, MenuItem } from "@material-ui/core";
import { useDispatch, useSelector } from "react-redux";
import { setTags } from "../actions/search";
const MultiChipSelect = ({ source }) => {
const dispatch = useDispatch();
const selectedTags = useSelector(state => state.search.tags);
const sourceTags = useSelector(state => state.settings[source].tags);
const handleTagClick = async (e) => {
dispatch(setTags(e.target.value));
};
return (
<Select
multiple
value={selectedTags}
onChange={handleTagClick}
input={<Input id="select-multiple" />}
// renderValue={selected => ( // This function is necessary when using objects, because the 'label' value should display the value.data value, not just the object. This is necessary because I'm using objects instead of strings.
// <div>
// {selected.map(value => (
// <Chip key={value._id} label={value.data}/>
// ))}
// </div>
// )}
>
>
{sourceTags.map(val => (
<MenuItem key={val._id} value={val}>
{val.data}
</MenuItem>
))}
</Select>
);
}
export default MultiChipSelect;
函数出现紧急输出:
package main
import (
"fmt"
"strconv"
)
var chanStr chan string
func main() {
go pump()
fmt.Println("iterating ...")
for val := range chanStr {
fmt.Printf("fetched val: %s from channel\n", val)
}
}
func pump() {
defer close(chanStr)
chanStr = make(chan string)
for i := 1; i <= 5; i++ {
fmt.Printf("pumping seq %d into channel\n", i)
chanStr <- "val" + strconv.Itoa(i)
}
//close(chanStr)
}
但是,如果我注释defer语句并在goroutine iterating ...
pumping seq 1 into channel
pumping seq 2 into channel
fetched val: val1 from channel
......
fetched val: val4 from channel
pumping seq 5 into channel
panic: close of nil channel
goroutine 5 [running]:
main.pump()
C:/personal/gospace/go-rules/test.go:26 +0x1a6
created by main.main
C:/personal/gospace/go-rules/test.go:11 +0x4e
中的for循环之后立即关闭,则接收方不会惊慌。
这两种情况有什么区别?看起来defer在收到值之前关闭了通道,但是常规关闭等待。
此外,当我使用比赛检测器进行构建时,即使在常规关闭中,它也会标记潜在的比赛条件(我无法每次都重新创建比赛)。是否暗示这两种方式都不适合正常关闭通道?
更新:
对于所有这些评论,我知道出了什么问题。我必须在pump
函数的第一行中创建通道。但是我在带有go1.12的Windows上运行,并且观察到了这种现象。
显然我没有伪造输出。我一直使用defer语句重新创建恐慌,即使在main()
答案 0 :(得分:3)
您的代码以不同的方式非常活泼:
在goroutine实际初始化通道之前,您有可能(实际上很有可能)从for val
循环中的通道开始读取,从而导致死锁。 / p>
iterating ...
pumping seq 1 into channel
fatal error: all goroutines are asleep - deadlock!
实际上,这是我观察到的唯一在本地或在操场上执行代码的行为。
如果我添加了延迟,
fmt.Println("iterating ...")
time.Sleep(10 * time.Millisecond) // Delay ensures the channel has been created
for val := range chanStr {
然后我会观察您注意到的行为
iterating ...
pumping seq 1 into channel
fetched val: val1 from channel
pumping seq 2 into channel
pumping seq 3 into channel
fetched val: val2 from channel
fetched val: val3 from channel
pumping seq 4 into channel
pumping seq 5 into channel
fetched val: val4 from channel
fetched val: val5 from channel
panic: close of nil channel
这样做的原因是您在close(chanStr)
仍为nil时正在调用chanStr
。如果您在创建频道后致电defer
,
func pump() {
chanStr = make(chan string)
defer close(chanStr)
您将解决该问题。
要解决这两个种族,您需要在调用goroutine之前初始化通道 。完整的代码:
package main
import (
"fmt"
"strconv"
)
var chanStr chan string
func main() {
chanStr = make(chan string)
go pump(chanStr)
fmt.Println("iterating ...")
for val := range chanStr {
fmt.Printf("fetched val: %s from channel\n", val)
}
}
func pump(chanStr chan string) {
defer close(chanStr)
for i := 1; i <= 5; i++ {
fmt.Printf("pumping seq %d into channel\n", i)
chanStr <- "val" + strconv.Itoa(i)
}
}
为进一步说明问题在于,defer close(chanStr)
立即评估chanStr
(虽然仍为nil
),请考虑以下(不推荐!)替代解决方案:
package main
import (
"fmt"
"strconv"
"time"
)
var chanStr chan string
func main() {
go pump()
fmt.Println("iterating ...")
time.Sleep(10 * time.Millisecond)
for val := range chanStr {
fmt.Printf("fetched val: %s from channel\n", val)
}
}
func pump() {
defer func() {
close(chanStr)
}()
chanStr = make(chan string)
for i := 1; i <= 5; i++ {
fmt.Printf("pumping seq %d into channel\n", i)
chanStr <- "val" + strconv.Itoa(i)
}
}
在这种情况下,延迟函数是chanStr
的闭包,因此chanStr
的求值被延迟到实际执行为止。在此版本中,当执行延迟函数时,chanStr
不再为nil,因此不会出现紧急情况。
答案 1 :(得分:0)
答案 2 :(得分:-1)
您发布的代码出现了死锁情况。正如Flimzy所指出的,您可能没有发布相同的代码。
这是应该起作用的更新代码:
package main
import (
"fmt"
"strconv"
)
func main() {
chanStr := make(chan string)
go pump(chanStr)
fmt.Println("iterating ...")
for val := range chanStr {
fmt.Printf("fetched val: %s from channel\n", val)
}
}
func pump(ch chan string) {
defer close(ch)
for i := 1; i <= 5; i++ {
fmt.Printf("pumping seq %d into channel\n", i)
ch <- "val" + strconv.Itoa(i)
}
//close(chanStr)
}
这种情况下的问题是,您在pump
函数内部创建了通道,因此主函数不知道如何使用数据,并且由于没有使用者,因此导致了死锁。