Daemon只执行一次goroutine。

huangapple go评论100阅读模式
英文:

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 (
&quot;bufio&quot;
&quot;flag&quot;
&quot;fmt&quot;
&quot;io/ioutil&quot;
&quot;os&quot;
&quot;syscall&quot;
&quot;time&quot;
&quot;github.com/sevlyar/go-daemon&quot;
)
var (
signal = flag.String(&quot;s&quot;, &quot;&quot;, `sdaemon -s ...
quit -- graceful shutdown`)
)
var (
stop = make(chan struct{})
done = make(chan struct{})
)
func main() {
flag.Parse()
daemon.AddCommand(daemon.StringFlag(signal, &quot;quit&quot;), syscall.SIGQUIT, TermHandler)
cntxt := &amp;daemon.Context{
PidFileName: &quot;/var/run/sdaemon.pid&quot;,
PidFilePerm: 0644,
WorkDir:     &quot;./&quot;,
Umask:       027,
Args:        []string{&quot;[sdaemon]&quot;},
}
if len(daemon.ActiveFlags()) &gt; 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(&quot;STOPPED!\n&quot;)
return
}
}
func Worker() {
for {
go Writer()
if _, ok := &lt;-stop; ok {
break
}
}
done &lt;- struct{}{}
}
func TermHandler(sig os.Signal) error {
stop &lt;- struct{}{}
if sig == syscall.SIGQUIT {
&lt;-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(&quot;$HOME/test&quot;)
contents := string(f)
fileHandle, _ := os.Create(&quot;$HOME/stest&quot;)
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()
    }
}

请注意,我将SleepWriter函数移动到了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 &lt;- stop:
// We could read a value from the channel
default:
// No value could be read, but we didn&#39;t block
}

So your Worker function should look something like:

func Worker() {
for {
time.Sleep(time.Minute)
select {
case &lt;- stop:
// Got a stop signal, stopping
done &lt;- 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

huangapple
  • 本文由 发表于 2017年5月29日 21:54:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/44244223.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定