英文:
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 receivecall 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论