我有以下代码,其中包含:
代码:
package main
import (
"log"
"os"
"os/signal"
"syscall"
"time"
)
func user_interupt_register() (writer_stop, reader_stop chan struct{}) {
writer_stop = make(chan struct{}, 1)
reader_stop = make(chan struct{}, 1)
signal_channel := make(chan os.Signal)
signal.Notify(signal_channel, syscall.SIGINT, syscall.SIGTERM)
go func() {
for sig := range signal_channel {
log.Println("Signal received: ", sig)
reader_stop <- struct{}{}
log.Println("Signal sent")
break
}
close(signal_channel)
log.Println("Closing User Interupt Register go routing")
}()
return writer_stop, reader_stop
}
func writer(item_id *uint32, data_channel chan uint32) {
log.Println("[WRITER] Item ID: ", *item_id)
data_channel <- *item_id
*item_id++
}
func reader(reader_stop, writer_stop chan struct{}, data_channel chan uint32) {
var flag = true
for flag {
select {
case <-reader_stop:
writer_stop <- struct{}{}
flag = false
case data, ok := <-data_channel:
if ok {
log.Println("[READER] Item ID: ", data)
}
default:
time.Sleep(1 * time.Second)
log.Println("[READER] No ID to print")
continue
}
}
log.Println("About to close(reader_stop)")
close(reader_stop)
log.Println("close(reader_stop) finished")
}
func main() {
log.Println("Starting Application")
data_channel := make(chan uint32)
defer close(data_channel)
writer_stop, reader_stop := user_interupt_register()
log.Println("Start Timer")
ticker := time.NewTicker(2 * time.Second)
go reader(reader_stop, writer_stop, data_channel)
var item_id uint32
item_id = 1
var flag = true
for flag {
select {
case <-ticker.C:
writer(&item_id, data_channel)
case <-writer_stop:
log.Println("About to stop timer")
ticker.Stop()
log.Println("Timer stopped")
flag = false
}
}
log.Println("About to close(writer_stop)")
close(writer_stop)
log.Println("close(writer_stop) finished")
log.Println("Exiting Application")
}
当我偶尔停止时,我会得到以下输出:
2016/07/19 16:02:46 Starting Application
2016/07/19 16:02:46 Start Timer
2016/07/19 16:02:47 [READER] No ID to print
2016/07/19 16:02:48 [WRITER] Item ID: 1
2016/07/19 16:02:48 [READER] No ID to print
2016/07/19 16:02:48 [READER] Item ID: 1
2016/07/19 16:02:49 [READER] No ID to print
2016/07/19 16:02:50 [WRITER] Item ID: 2
2016/07/19 16:02:50 [READER] No ID to print
2016/07/19 16:02:50 [READER] Item ID: 2
2016/07/19 16:02:51 [READER] No ID to print
^C2016/07/19 16:02:52 Signal received: interrupt
2016/07/19 16:02:52 Signal sent
2016/07/19 16:02:52 Closing User Interupt Register go routing
2016/07/19 16:02:52 [WRITER] Item ID: 3
2016/07/19 16:02:52 [READER] No ID to print
2016/07/19 16:02:52 About to close(reader_stop)
2016/07/19 16:02:52 close(reader_stop) finished
^Cpanic: send on closed channel
goroutine 5 [running]:
panic(0x4c1d00, 0xc8200980b0)
/usr/local/go/src/runtime/panic.go:481 +0x3e6
os/signal.process(0x7ffa8f64d078, 0xc8200980a0)
/usr/local/go/src/os/signal/signal.go:176 +0x17a
os/signal.loop()
/usr/local/go/src/os/signal/signal_unix.go:22 +0x75
created by os/signal.init.1
/usr/local/go/src/os/signal/signal_unix.go:28 +0x37
但是,大多数时候它会在以下输出时正常停止。
2016/07/19 16:03:13 Starting Application
2016/07/19 16:03:13 Start Timer
2016/07/19 16:03:14 [READER] No ID to print
2016/07/19 16:03:15 [WRITER] Item ID: 1
2016/07/19 16:03:15 [READER] No ID to print
2016/07/19 16:03:15 [READER] Item ID: 1
2016/07/19 16:03:16 [READER] No ID to print
^C2016/07/19 16:03:16 Signal received: interrupt
2016/07/19 16:03:16 Signal sent
2016/07/19 16:03:16 Closing User Interupt Register go routing
2016/07/19 16:03:17 [WRITER] Item ID: 2
2016/07/19 16:03:17 [READER] No ID to print
2016/07/19 16:03:17 [READER] Item ID: 2
2016/07/19 16:03:17 About to close(reader_stop)
2016/07/19 16:03:17 close(reader_stop) finished
2016/07/19 16:03:17 About to stop timer
2016/07/19 16:03:17 Timer stopped
2016/07/19 16:03:17 About to close(writer_stop)
2016/07/19 16:03:17 close(writer_stop) finished
2016/07/19 16:03:17 Exiting Application
那有什么不对?我怎样才能确定它会停止正常停止所有常规和关闭通道?
答案 0 :(得分:1)
您的代码中发生的事情是,一旦发送信号,一切都会有效,直到reader
函数结束。也就是说,信号被发送到writer_stop
(语句writer_stop <- struct{}{}
被执行。
问题在于您的select
为主。您正在writer_stop
的{{1}}上阅读近似值。
case
由于 select {
case <-ticker.C:
writer(&item_id, data_channel)
case <-writer_stop:
log.Println("About to stop timer")
ticker.Stop()
log.Println("Timer stopped")
flag = false
}
的性质,如果准备选择多个select
,cases
会随机选择一个。{/ p>
现在,在您的select
之后,在调用select
之后,由于错误的同步而导致错误的同步,writer_stop <- struct{}{}
被选中并且case <-ticker.C:
被调用。
此时writer
已返回,这是reader
data_channel
writer
阻止data_channel <- *item_id
的唯一方法,导致程序挂起。
发生错误panic: send on closed channel
是因为您再次点击^C
并且信号频道已关闭。
您可以通过多种方式解决此问题。一个简单的解决方案是向data_channel添加缓冲区:
data_channel := make(chan uint32, 1)
这是实现此目的的一种方法:
func user_interupt_register() (writer_stop, reader_stop chan struct{}) {
writer_stop = make(chan struct{}, 1)
// same as before
go func() {
sig := <-signal_channel
writer_stop <- struct{}{} // change to writer_stop
// same as before
}()
return writer_stop, reader_stop
}
func reader(data_channel chan uint32) {
for item := range data_channel {
log.Println("[READER] Item ID: ", item)
}
}
func main() {
log.Println("Starting Application")
data_channel := make(chan uint32)
// defer close(data_channel)
writer_stop, _ := user_interupt_register()
log.Println("Start Timer")
ticker := time.NewTicker(2 * time.Second)
readTicker := time.NewTicker(1 * time.Second)
go reader(data_channel)
go func() { // to print intermittent message
for _ = range readTicker.C {
data_channel <- 0 // some insignificant value
}
}()
var item_id uint32 = 1
for _ = range ticker.C {
select {
case <-writer_stop:
data_channel <- item_id
close(data_channel)
ticker.Stop()
readTicker.Stop()
return
default:
data_channel <- item_id
item_id++
}
}
close(writer_stop)
}
请注意,这需要通过测试。
答案 1 :(得分:1)
IMO你应该改变你的读者/作者结构。你不需要只为作者停止读者。
当前结构的问题是可能存在将要写入且从未读取的项目。这可能就是为什么你不想让data_channel成为缓冲通道的原因。但是仍有一个项目可能无法读取已写入该频道的内容。
您只需要作家的停止频道。如果该通道发出信号并关闭数据通道,则写入器停止。读者没有选择,只有<ul>
@foreach($foods as $food)
<li>{{$food->name}}</li>
@endforeach
</ul></pre>
。如果data_channel已关闭,则阅读器将清空其中的现有项目并自行停止。
替换它:
for item := range data_channel
使用:
func reader(reader_stop, writer_stop chan struct{}, data_channel chan uint32) {
var flag = true
for flag {
select {
case <-reader_stop:
writer_stop <- struct{}{}
flag = false
case data, ok := <-data_channel:
if ok {
log.Println("[READER] Item ID: ", data)
}
default:
time.Sleep(1 * time.Second)
log.Println("[READER] No ID to print")
continue
}
}
log.Println("About to close(reader_stop)")
close(reader_stop)
log.Println("close(reader_stop) finished")
}
卸下:
func reader(writer_stop chan struct{}, data_channel chan uint32) {
for item := range data_channel {
log.Println("[READER] Item ID: ", data)
}
}
并在此处添加:
defer close(data_channel)
然后你需要一些等待读者完成的东西。查看case <-writer_stop:
close(data_channel)
log.Println("About to stop timer")
ticker.Stop()
log.Println("Timer stopped")
flag = false
。更好的方法是将作者放入常规程序并将读者放入主程序中。这样,如果读者完成,你的主程序就会停止(如果它没有从作者那里接收到任何更多的值 - 而作者关闭了频道,则会停止)。
这里我的工作建议尽量少变:
sync.WaitGroup
您不会收到package main
import (
"log"
"os"
"os/signal"
"syscall"
"time"
)
func user_interupt_register() (writer_stop chan struct{}) {
writer_stop = make(chan struct{}, 1)
signal_channel := make(chan os.Signal)
signal.Notify(signal_channel, syscall.SIGINT, syscall.SIGTERM)
go func() {
for sig := range signal_channel {
log.Println("Signal received: ", sig)
writer_stop <- struct{}{}
log.Println("Signal sent")
break
}
close(signal_channel)
log.Println("Closing User Interupt Register go routing")
}()
return
}
func writer(item_id *uint32, data_channel chan uint32, writer_stop chan struct{}) {
log.Println("Start Timer")
ticker := time.NewTicker(2 * time.Second)
var flag = true
for flag {
select {
case <-ticker.C:
log.Println("[WRITER] Item ID: ", *item_id)
data_channel <- *item_id
*item_id++
case <-writer_stop:
close(data_channel)
log.Println("About to stop timer")
ticker.Stop()
log.Println("Timer stopped")
flag = false
}
}
}
func main() {
log.Println("Starting Application")
data_channel := make(chan uint32)
writer_stop := user_interupt_register()
var item_id uint32
item_id = 1
go writer(&item_id, data_channel, writer_stop)
for data := range data_channel {
log.Println("[READER] Item ID: ", data)
}
log.Println("About to close(writer_stop)")
close(writer_stop)
log.Println("close(writer_stop) finished")
log.Println("Exiting Application")
}
消息,否则会产生相同的结果。
注意:其他代码需要相应调整...就像删除writer_stop频道一样。