如何调度 运行 非阻塞函数

How to schedule running non-blocking functions

我的问题是如何在每个时间间隔 N 调度 运行ning 个独立的非阻塞函数。

我最初的方法是在 select 语句中使用 go channels 以非阻塞方式接收值,并在每个函数中使用 time.Sleep(N) 来安排调用。

在下面的代码片段中,这仅适用于第一个 运行;但是,在第一次调用之后,它会不断调用 computeY(),而不考虑 time.Sleep() 调用。

    package main

    import (
        "fmt"
        "time"
    )

    var (
        x string = ""
        y string = ""
    )

    func computeY(c chan string) {
        time.Sleep(10 * time.Second)
        fmt.Println("I'm in Y")

        y = "this is Y value"
        c <- y
    }

    func computeX(c chan string) {
        time.Sleep(1 * time.Second)

        x = "this is X value"
        c <- x
    }

    func main() {
        xC := make(chan string)
        yC := make(chan string)

        for {
            go computeX(xC)
            go computeY(yC)

            select {
            case x := <-xC:
                fmt.Println(fmt.Sprintf("X: %v, Y: %v", x, y))
            case y := <-yC:
                fmt.Println(fmt.Sprintf("X: %v, Y: %v", x, y))
            }

        }
    }

您在循环的每次迭代中都调用了 computeXcomputeY

因为computeX需要1秒,所以for循环每秒迭代一次,并且在yC获取一个值时有一个额外的时间。

这意味着您 运行 go computeYt=0st=1st=2s 等.... 第一个终止于 t=10s,第二个终止于 t=11s,依此类推...

如果您想确保一次只安排一个 computeXcomputeY,您需要将 main 更改为以下内容:

    go computeX(xC)
    go computeY(yC)
    for {
        select {
        case x = <-xC:
            fmt.Printf("Finished computeX: X: %v, Y: %v\n", x, y)
            go computeX(xC)
        case y = <-yC:
            fmt.Printf("Finished computeY: X: %v, Y: %v\n", x, y)
            go computeY(yC)
        }
    } 

关于您的代码的其他一些注意事项:

  • xy 是全局的,分配在 computeXcomputeY
  • 您的频道显示阴影 xy
  • fmt.Println(fmt.Sprintf("...")) 就是 fmt.Printf("...\n")
  • 您不需要将字符串初始化为 "",这是默认值

虽然 @Marc 的回答解释了您的代码问题并展示了如何修复它,但我会尝试为您提供一些有关调度函数的模式。

模式 1:time.Ticker

A Ticker holds a channel that delivers `ticks' of a clock at intervals.

示例:

func Schedule(interval time.Duration,f func()) {
    t:=time.NewTimer(interval)
    go func() {
        for {
            <-t.C
            f()
        }
    }()
 }

模式二:time.AfterFunc

AfterFunc waits for the duration to elapse and then calls f in its own goroutine. It returns a Timer that can be used to cancel the call using its Stop method.

示例:

func Schedule(interval time.Duration,f func()) {
    var wrap func()
    wrap = func() {
        f()
        time.AfterFunc(wrap)
    }
    time.AfterFunc(f)
}

模式 1 更具可读性和表现力,而模式 2 在内存方面更高效。