使用上下文来永久停止for循环的Go代码。

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

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 &lt;-ctx.Done:
    return
  case &lt;-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 &lt;-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 &lt;-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 (
	&quot;fmt&quot;
	&quot;sync/atomic&quot;
	&quot;time&quot;
)

func asyncForeverTask(stop *atomic.Bool) {
	for {
		if stop.Load() {
			return
		}
		fmt.Println(&quot;I&#39;m still running&quot;)
		time.Sleep(1 * time.Second)
	}
}

func main() {
	var stop atomic.Bool
	go asyncForeverTask(&amp;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 (
    &quot;fmt&quot;
    &quot;time&quot;
)

var done bool = false

func asyncForeverTask() {
    for {
        if done {
            return
        }
        fmt.Println(&quot;I&#39;m still running&quot;)
        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.

huangapple
  • 本文由 发表于 2023年7月7日 04:23:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76632304.html
匿名

发表评论

匿名网友

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

确定