英文:
Daemon executes only one time a goroutine
问题
我尝试添加必要的代码来将我的应用程序执行为守护进程。我使用了下面的项目:
- github.com/sevlyar/go-daemon
我重写了示例go代码,代码如下:
https://github.com/sevlyar/go-daemon/blob/master/sample/sample.go
package main
import (
"bufio"
"flag"
"fmt"
"io/ioutil"
"os"
"syscall"
"time"
"github.com/sevlyar/go-daemon"
)
var (
signal = flag.String("s", "", `sdaemon -s ...
quit -- graceful shutdown`)
)
var (
stop = make(chan struct{})
done = make(chan struct{})
)
func main() {
flag.Parse()
daemon.AddCommand(daemon.StringFlag(signal, "quit"), syscall.SIGQUIT, TermHandler)
cntxt := &daemon.Context{
PidFileName: "/var/run/sdaemon.pid",
PidFilePerm: 0644,
WorkDir: "./",
Umask: 027,
Args: []string{"[sdaemon]"},
}
if len(daemon.ActiveFlags()) > 0 {
d, _ := cntxt.Search()
daemon.SendCommands(d)
return
}
d, err := cntxt.Reborn()
if d != nil {
return
}
if err != nil {
os.Exit(1)
}
defer cntxt.Release()
// Start daemon
go Worker()
err = daemon.ServeSignals()
if err != nil {
fmt.Printf("STOPPED!\n")
return
}
}
func Worker() {
for {
go Writer()
if _, ok := <-stop; ok {
break
}
}
done <- struct{}{}
}
func TermHandler(sig os.Signal) error {
stop <- struct{}{}
if sig == syscall.SIGQUIT {
<-done
}
return daemon.ErrStop
}
我添加了一个名为Writer()
的函数,它读取一个文件,将文本保存为字符串,并使用该字符串创建一个新文件。
func Writer() error {
time.Sleep(time.Minute)
f, _ := ioutil.ReadFile("$HOME/test")
contents := string(f)
fileHandle, _ := os.Create("$HOME/stest")
writer := bufio.NewWriter(fileHandle)
defer fileHandle.Close()
fmt.Fprintln(writer, contents)
writer.Flush()
return nil
}
我在处理golang中的通道方面不太熟练,也不知道为什么Worker()
函数中的无限循环只执行一次...
你能帮助我吗?
英文:
I try to add the necessary code to execute my app like a daemon. I used the next project:
- github.com/sevlyar/go-daemon
I rewrote the sample go code that it's done:
https://github.com/sevlyar/go-daemon/blob/master/sample/sample.go
package main
import (
"bufio"
"flag"
"fmt"
"io/ioutil"
"os"
"syscall"
"time"
"github.com/sevlyar/go-daemon"
)
var (
signal = flag.String("s", "", `sdaemon -s ...
quit -- graceful shutdown`)
)
var (
stop = make(chan struct{})
done = make(chan struct{})
)
func main() {
flag.Parse()
daemon.AddCommand(daemon.StringFlag(signal, "quit"), syscall.SIGQUIT, TermHandler)
cntxt := &daemon.Context{
PidFileName: "/var/run/sdaemon.pid",
PidFilePerm: 0644,
WorkDir: "./",
Umask: 027,
Args: []string{"[sdaemon]"},
}
if len(daemon.ActiveFlags()) > 0 {
d, _ := cntxt.Search()
daemon.SendCommands(d)
return
}
d, err := cntxt.Reborn()
if d != nil {
return
}
if err != nil {
os.Exit(1)
}
defer cntxt.Release()
// Start daemon
go Worker()
err = daemon.ServeSignals()
if err != nil {
fmt.Printf("STOPPED!\n")
return
}
}
func Worker() {
for {
go Writer()
if _, ok := <-stop; ok {
break
}
}
done <- struct{}{}
}
func TermHandler(sig os.Signal) error {
stop <- struct{}{}
if sig == syscall.SIGQUIT {
<-done
}
return daemon.ErrStop
}
I have added a function Writer()
that read a file, keep the text like a string and create a new file with this string.
func Writer() error {
time.Sleep(time.Minute)
f, _ := ioutil.ReadFile("$HOME/test")
contents := string(f)
fileHandle, _ := os.Create("$HOME/stest")
writer := bufio.NewWriter(fileHandle)
defer fileHandle.Close()
fmt.Fprintln(writer, contents)
writer.Flush()
return nil
}
I don't handle so good the channels in golang and I don't know why the infinite loop for in Worker()
function is executed only once...
Can you help me please?
答案1
得分: 1
问题出在Worker
函数中,当你尝试检查done
通道中是否有任何数据时。receive
调用将会阻塞,直到有值可读取,因此该调用将一直阻塞,直到你向进程发送一个信号。
从receive
操作符返回的第二个值ok
并不表示是否成功读取了一个值。它只表示在尝试接收一个值时通道是否已关闭(如果是,则返回零值
,请参阅规范)。
要检查通道中是否有值,你需要使用select
语句,像这样:
select {
case v, ok := <-stop:
// 我们可以从通道中读取一个值
default:
// 无法读取到值,但我们没有阻塞
}
所以你的Worker
函数应该像这样:
func Worker() {
for {
time.Sleep(time.Minute)
select {
case <-stop:
// 收到停止信号,停止循环
done <- struct{}{}
return
default:
// 没有停止信号,继续循环
}
go Writer()
}
}
请注意,我将Sleep
从Writer
函数移动到了Worker
函数中,否则你将会得到成千上万个并发的Writer
Go协程...
英文:
The problem is in the Worker
function, when you try to check if you have any data in the done
channel. The receive
call will block until there is a value to be read, so that call will block until you send a signal to the process.
The second value returned from the receive operator, ok
, does not indicate whether a value was succesfully read or not. It just indicates if the channel was closed when trying to receive a value (if so the zero value
is returned, see the specification).
To check if there is a value in the channel you need to use a select
statement, like this:
select {
case v, ok <- stop:
// We could read a value from the channel
default:
// No value could be read, but we didn't block
}
So your Worker
function should look something like:
func Worker() {
for {
time.Sleep(time.Minute)
select {
case <- stop:
// Got a stop signal, stopping
done <- struct{}{}
return
default:
// No stop signal, continuing loop
}
go Writer()
}
}
Note that I have moved the Sleep
from the Writer
function to the Worker
, otherwise you would end up with thousands of concurrent Writer
go-routines...
1: https://golang.org/ref/spec#Receive_operator
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论