用Golang启动、暂停、恢复和终止一个goroutine

848 阅读2分钟

在这个例子中,我们将运行一个goroutine,每秒钟在终端上打印一个点。重要的是,我们将与它进行手动交互。我们将启动、暂停、恢复和终止它。虽然它的工作原理和预期的一样,但它可以被改进,可能的改进已经被添加到代码的顶部,但我们现在要忽略它。这只是一个最基本的骨架。

例子

package main

import (
	"log"
	"runtime"
	"time"
)

/**
The initial state of a work can only be started.
If there is no work started yet then obviously there is nothing to be paused/resumed/terminated.

Valid state progressions:
	start	> pause
			> terminate
	pause	> resume
			> terminate
	resume	> pause
			> terminate

Based on the progression rules above, the previous state should be recorded and checked against the
new state. If it is an invalid progression, it should be handled appropriately. e.g., nop with a warning
 */
type state string

const (
	start     state = "started"
	pause     state = "paused"
	resume    state = "resumed"
	terminate state = "terminated"
)

func main() {
	signaller := make(chan state)

	// This will print 1 because main() function is a goroutine
	log.Println(runtime.NumGoroutine())

	go handler(signaller)

	// This will print 2 because we started handler() goroutine above
	log.Println(runtime.NumGoroutine())

	signaller<- start
	time.Sleep(time.Second*3)

	// This will print 3 because handler() goroutine will start work() goroutine
	log.Println(runtime.NumGoroutine())

	signaller<- pause
	time.Sleep(time.Second*5)

	// This will print 2 because handler() goroutine will stop work() goroutine
	log.Println(runtime.NumGoroutine())

	signaller<- resume
	time.Sleep(time.Second*3)

	// This will print 3 because handler() goroutine will again start work() goroutine
	log.Println(runtime.NumGoroutine())

	signaller<- terminate
	time.Sleep(time.Second*3)

	// This will print 1 because handler() goroutine will stop work() goroutine and then itself
	log.Println(runtime.NumGoroutine())
}

func handler(signaller chan state) {
	done := make(chan struct{})

	for {
		signal := <-signaller

		switch signal {
		case start:
			log.Println(signal)
			go work(done)
		case pause:
			done<- struct{}{}
			log.Println(signal)
		case resume:
			log.Println(signal)
			go work(done)
		case terminate:
			done<- struct{}{}
			log.Println(signal)
			return
		default:
			log.Println("unknown signal")
			return
		}
	}
}

func work(done <-chan struct{}) {
	for {
		select {
		case <-done:
			return
		default:
			time.Sleep(time.Second)
			log.Println(".")
		}
	}
}

测试

$ go run -race main.go 
2020/11/14 12:43:14 1
2020/11/14 12:43:14 2
2020/11/14 12:43:14 started
2020/11/14 12:43:15 .
2020/11/14 12:43:16 .
2020/11/14 12:43:17 3
2020/11/14 12:43:17 .
2020/11/14 12:43:17 paused
2020/11/14 12:43:22 2
2020/11/14 12:43:22 resumed
2020/11/14 12:43:23 .
2020/11/14 12:43:24 .
2020/11/14 12:43:25 3
2020/11/14 12:43:25 .
2020/11/14 12:43:25 terminated
2020/11/14 12:43:28 1