英文:
Go - Stopping forever for loop with context
问题
我有一个永远执行的异步进程,代码如下:
func asyncForeverTask() {
for {
anyTask()
}
}
func main() {
go asyncForeverTask()
server.run(8080)
}
当服务器接收到特定请求时,我希望能够停止这个无限循环。
我知道我需要使用context.WithCancel()
,但我不知道如何集成它使其正常工作。我知道以下语法:
for {
select {
case <-ctx.Done():
return
case <-otherCh:
// do smh.
}
}
但是asyncForeverTask
不是由来自otherCh
的信号运行的,它会一直运行下去。我是Go的新手,希望能得到任何形式的帮助。
英文:
I have an async process which should go on forever thats looking like this:
func asyncForeverTask() {
for {
anyTask()
}
}
func main() {
go asyncForeverTask()
server.run(8080)
}
I would like to be able to stop this for loop when a certain request request comes to the server.
I am aware that I need to use the context.WithCancel()
, but I do not know how to integrate it for it to work. I know the following syntax:
for {
select {
case <-ctx.Done:
return
case <-otherCh:
// do smh.
}
}
but the asyncForeverTask
is not run by a signal from any otherCh, but it runs forever. I am a noobie to go and would appreciate any type of help.
答案1
得分: 4
你可以使用一个通道来实现这个功能:
var stopAsync = make(chan struct{})
func asyncForeverTask() {
for {
select {
case <-stopAsync:
return
default:
}
anyTask()
}
}
要取消任务,只需关闭通道。你必须确保通道只关闭一次,否则会引发 panic。
你也可以使用上下文来实现:
func main() {
ctx, cancel := context.WithCancel(context.Background())
go asyncForeverTask(ctx)
...
}
func asyncForeverTask(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
}
anyTask()
}
}
或者可以这样实现:
func asyncForeverTask(ctx context.Context) {
for {
if ctx.Err() != nil {
return
}
anyTask()
}
要停止任务,调用返回的 cancel
函数:
...
cancel()
如果你需要同时停止 anyTask
,你必须定期检查通道关闭或上下文关闭,并从任务中返回。
英文:
You can do this with a channel:
var stopAsync = make(chan struct{})
func asyncForeverTask() {
for {
select {
case <-stopAsync:
return
default:
}
anyTask()
}
}
To cancel, simply close the channel. You have to make sure channel is closed only once, otherwise you'll get a panic.
You can also do this with a context:
func main() {
ctx,cancel:=context.WithCancel(context.Background())
go asyncForeverTask(ctx)
...
}
func asyncForeverTask(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
}
anyTask()
}
}
Alternatively:
func asyncForeverTask(ctx context.Context) {
for {
if ctx.Err()!=nil {
return
}
anyTask()
}
To stop, call the cancel
function returned:
...
cancel()
If you need to stop the anyTask
as well, you have to check for the channel closure or context closure every so often and return from the task.
答案2
得分: 2
通道和上下文是可行且方便的方法,但它们并不是你唯一的选择。对于像这样的情况,一个简单的atomic.Bool
可能更简单。可以像这样使用:
package main
import (
"fmt"
"sync/atomic"
"time"
)
func asyncForeverTask(stop *atomic.Bool) {
for {
if stop.Load() {
return
}
fmt.Println("我还在运行")
time.Sleep(1 * time.Second)
}
}
func main() {
var stop atomic.Bool
go asyncForeverTask(&stop)
time.Sleep(10 * time.Second)
stop.Store(true)
}
英文:
Channels and contexts work and can be handy. But they're not your only option. For something like this a simple atomic.Bool
might be simpler. Something like:
package main
import (
"fmt"
"sync/atomic"
"time"
)
func asyncForeverTask(stop *atomic.Bool) {
for {
if stop.Load() {
return
}
fmt.Println("I'm still running")
time.Sleep(1 * time.Second)
}
}
func main() {
var stop atomic.Bool
go asyncForeverTask(&stop)
time.Sleep(10 * time.Second)
stop.Store(true)
}
答案3
得分: 0
信不信由你,但这种简单的闭包方法也可以工作:
package main
import (
"fmt"
"time"
)
func main() {
done := make(chan bool)
go func() {
for {
select {
case <-done:
return
default:
fmt.Println("I'm still running")
time.Sleep(1 * time.Second)
}
}
}()
time.Sleep(10 * time.Second)
done <- true
}
是的,我知道全局变量是邪恶的。只是为了记录。
英文:
Believe it or not, but this simple closure approach works too:
package main
import (
"fmt"
"time"
)
var done bool = false
func asyncForeverTask() {
for {
if done {
return
}
fmt.Println("I'm still running")
time.Sleep(1 * time.Second)
}
}
func main() {
go asyncForeverTask()
time.Sleep(10 * time.Second)
done = true
}
Yes, I know global variables are evil. Just for a record.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论