如何安排运行非阻塞功能

时间:2018-01-20 16:00:44

标签: multithreading go goroutine

我的问题是如何安排每个间隔N运行独立的非阻塞函数。

我最初的方法是在select语句中使用go通道以非阻塞方式接收值,并在每个函数中使用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))
            }

        }
    }

2 个答案:

答案 0 :(得分:2)

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

由于computeX需要1,因此for循环每秒迭代一次,yC得到一个值的额外时间。

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

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

    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")
  • 您不需要将字符串初始化为"",这是默认值

答案 1 :(得分:0)

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

模式1:time.Ticker

  

Ticker拥有一个提供`ticks&#39;每隔一段时间。

示例:

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

模式2:time.AfterFunc

  

AfterFunc等待持续时间过去,然后在自己的goroutine中调用f。它返回一个Timer,可用于使用Stop方法取消呼叫。

示例:

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

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