Go 优雅地终止读卡器进程的基于定时器的写入和执行例程

Go 优雅地终止读卡器进程的基于定时器的写入和执行例程,go,goroutine,Go,Goroutine,我有以下代码,其中包含: 基于代码的编写器 按常规阅读 用于编写器和读取器通信的数据通道 停止写入器和停止读取器通道,用于优雅地停止写入器和读取器 用于处理“Ctrl-C”用户输入的信号处理程序 代码: 但是,大多数情况下,它会随着以下输出正常停止 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/0

我有以下代码,其中包含:

  • 基于代码的编写器
  • 按常规阅读
  • 用于编写器和读取器通信的数据通道
  • 停止写入器和停止读取器通道,用于优雅地停止写入器和读取器
  • 用于处理“Ctrl-C”用户输入的信号处理程序
代码:

但是,大多数情况下,它会随着以下输出正常停止

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

那怎么了?我如何确保它将正常停止所有go例程并关闭频道?

您的代码中发生的情况是,一旦发送信号,一切都将正常工作,直到
读取器
功能结束。也就是说,信号被发送到
writer\u stop
(语句
writer\u stop在我看来,您应该更改读写器结构。您不需要停止读写器,只需停止写写器即可

当前结构的问题是,可能存在将被写入而从不读取的项。这可能是您不希望将data_通道设置为缓冲通道的原因。但仍有一项可能无法读取已写入通道的项

写入程序只需要一个停止通道。如果该通道发出信号并关闭数据通道,写入程序将停止。读卡器没有选择,只有一个
用于项:=范围数据通道
。如果数据通道关闭,读卡器将清空其中的现有项并自行停止

替换此项:

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")
}
删除:

defer close(data_channel)
并将其添加到此处:

case <-writer_stop:
   close(data_channel)
   log.Println("About to stop timer")
   ticker.Stop()
   log.Println("Timer stopped")
   flag = false
您将不会有
log.Println(“[READER]没有要打印的ID”)
消息,否则它将产生相同的结果


注意:其他代码需要相应地调整…例如删除writer\u stop通道。

谢谢。但是我不能更改数据通道以限制。对于第二个解决方案,您能解释更多吗?因为如果我将write函数移动到默认部分,它将不会在给定时间写入。或者我错了?@abhink:我不知道我不明白。这个错误说的是在一个封闭的频道上发送。它不是挂起的,是吗?@TehSphinX它是挂起的。当我第二次点击“Ctrl-C”时它会惊慌失措。@TehSphinX代码挂起。在用于停止程序的错误之前,您可以看到第二个
^C
。这是因为信号通道已关闭。正确,因为读卡器是使用者,它依赖于写卡器。请确保写卡器从不阻塞并且已正常关闭。然后您应该能够管理读卡器r正确。它是否说它试图在哪一行写入封闭通道?你能添加该信息吗?OP使用
标志来终止循环,所以他实际上需要它:)如果我用你的建议替换它,我将无法每2秒写入一次。@ain:哦,是的,我没有意识到。可以使用
中断
完成@凤凰城:看到我刚才在答案中加的注释2了吗?但是我根本不懂你的注释2。读者每2秒钟阅读一次是什么意思。我需要作家每2秒钟给读者写信。Break可以与mark一起使用。但我更喜欢和国旗一起使用。反正也没关系。@TehSphinX
data\u通道
被写入,而不是从中读取@凤凰城:@TehSphinx的意思可能是,你可以在读卡器中调整范围循环,而不是写。所以writer仍然每3秒写一次,但现在读卡器可以在
数据通道上循环。我仍然认为问题在于中断的处理方式。
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)
}
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
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")
}