Golang 中主流程要控制某个协程的暂停与继续,需要两个通道分别接收来自主流程的通知,并在协程中始终监听这两个通知。例如:
package main
import (
"fmt"
"time"
)
func main() {
// 创建一个通道
chPause := make(chan struct{})
chResume := make(chan struct{})
// 启动一个 goroutine
go func() {
// 初始化 isPaused
isPaused := false
// 等待通知
for {
select {
case <-chPause:
// 暂停执行
isPaused = true
fmt.Println("已暂停。")
case <-chResume:
// 继续执行
isPaused = false
fmt.Println("继续执行。")
default:
// 暂停执行
if isPaused {
continue
}
// 执行任务
fmt.Println("执行任务...")
time.Sleep(time.Millisecond * 500)
}
}
}()
// 发送通知
time.Sleep(time.Second * 2)
chPause <- struct{}{}
time.Sleep(time.Second * 2)
chResume <- struct{}{}
time.Sleep(time.Second)
}
以上代码中,对于已经开始执行的任务无法暂停,只能做到收到暂停通知后不再继续执行后续任务。再考虑到chPause
和chResume
均为无缓冲通道,这意味着任务未执行完毕时,暂时不接收chPause
和chResume
,即发送端会被阻塞。此时,可以认为,只要暂停信号成功送入,即表示之前的任务已暂停,且不会执行后续的任务。若之前任务未暂停,则需要待之前的任务执行完毕后才会送入。即,暂停信号的送入和成功暂停是同步的。
如果想要无阻塞送入暂停信号,可以为chPause
设置缓冲,并在成功发送通知后阻止再次送入信号。
相应地,由于送入暂停信号和实际暂停相分离,需要设置回调机制通知送入信号一方已成功暂停,以实现送入暂停信号和成功暂停的完整异步逻辑。
例如:
package main
import (
"context"
"fmt"
"sync"
"time"
)
type CoroutineControl struct {
pauseChan chan struct{}
resumeChan chan struct{}
pauseNotify chan struct{}
resumeNotify chan struct{}
isPaused bool
mu sync.Mutex
}
func NewCoroutineControl() *CoroutineControl {
return &CoroutineControl{
pauseChan: make(chan struct{}),
resumeChan: make(chan struct{}),
pauseNotify: make(chan struct{}),
resumeNotify: make(chan struct{}),
}
}
func (cc *CoroutineControl) Run(ctx context.Context) {
go func() {
defer fmt.Println("canceled.")
for {
select {
case <-cc.pauseChan:
cc.mu.Lock()
cc.isPaused = true
fmt.Println("Paused.")
close(cc.pauseNotify)
cc.mu.Unlock()
select {
case <-cc.resumeChan:
cc.mu.Lock()
cc.isPaused = false
fmt.Println("Resumed.")
close(cc.resumeNotify)
cc.mu.Unlock()
case <-ctx.Done():
return
}
case <-ctx.Done():
return
default:
cc.mu.Lock()
if !cc.isPaused {
fmt.Println("Working...")
}
cc.mu.Unlock()
time.Sleep(time.Millisecond * 500)
}
}
}()
}
func (cc *CoroutineControl) Pause() {
cc.mu.Lock()
if !cc.isPaused {
cc.pauseNotify = make(chan struct{})
cc.mu.Unlock()
cc.pauseChan <- struct{}{}
<-cc.pauseNotify
} else {
cc.mu.Unlock()
}
}
func (cc *CoroutineControl) Resume() {
cc.mu.Lock()
if cc.isPaused {
cc.resumeNotify = make(chan struct{})
cc.mu.Unlock()
cc.resumeChan <- struct{}{}
<-cc.resumeNotify
} else {
cc.mu.Unlock()
}
}
func main() {
control := NewCoroutineControl()
ctx, cancel := context.WithCancel(context.Background())
control.Run(ctx)
time.Sleep(time.Second)
control.Pause()
time.Sleep(time.Second)
control.Resume()
time.Sleep(time.Second * 2)
control.Pause()
fmt.Println("waiting for being canceled.")
// 模拟在其他地方调用cancel来取消协程
time.Sleep(time.Second * 2)
cancel()
// 等待协程退出
time.Sleep(time.Second)
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。