有没有办法阻止长时间阻塞功能?

时间:2013-12-06 17:00:44

标签: go

我有一个运行几分钟的功能,我正试图找到一种方法来阻止它使用频道。

我认为我无法像以下代码那样执行此操作,因为我认为select只会在stop完成后处理default个案。

package main

import (
    "fmt"
    "time"
)

func main() {
    stop := make(chan int)

    go func() {
        for {
            select {
            case <-stop:
                fmt.Println("return")
                return
            default:
                fmt.Println("block")
                time.Sleep(5 * time.Second) // simulate a long running function
                fmt.Println("unblock")
            }
        }
    }()

    time.Sleep(1 * time.Second)
    stop <- 1
}

3 个答案:

答案 0 :(得分:6)

我认为你不能这样做:Go中的goroutines在某种意义上是合作的:直到goroutine 主动试图以某种方式弄清楚它是否应该退出,没有办法强制它这样做。

我说这是一个功能实际上是因为如果你可以强行收获一个长期运行的goroutine,你将无法确定它是否已经干净利落,即正确释放它所获得的所有资源。

所以要么活着(比如说,如果你的过程想要退出,只需等待那个goroutine完成)或重组它,以便它定期检查是否有信号退出。或者甚至考虑将它执行的任务卸载到外部进程(但请注意,虽然在释放从操作系统获取的资源方面杀死进程是安全的,但对于进程可能已更新的外部数据而言,这是不安全的 - 比如文件)。

答案 1 :(得分:4)

我认为你不能结束goroutine,但你可以换成另一个。 您可以通过将函数包装在一个完成后将数据发送到通道的goroutine中来使函数超时。然后需要选择等待返回的频道或超时的频道。

实施例

package main

import (
    "fmt"
    "time"
)

func waitForMe(){
        time.Sleep(time.Second*5)
}
func main(){
        c1 := make(chan string, 1)
        go func(){
                waitForMe()
                c1 <- "waitForMe is done"
        }()
        select {
        case res := <-c1:
                fmt.Println(res)
        case <-time.After(time.Second*2):
                fmt.Println("timed out")
        }
}

请注意,每当您拨打selecttime.After()或拥有阻止频道时,goroutines会切换到下一个可用的goroutine。

以下是您的计划正在发生的事情。

您使用Goroutine语句的程序

package main

import (
    "fmt"
    "time"
)

func main() {
    stop := make(chan int)
    go func() {
       fmt.Println("Goroutine B before for loop")
        for {
            fmt.Println("Goroutine B inside for loop")
            select {
            case <-stop:
                fmt.Println("return")
                return
            default:
                fmt.Println("Goroutine B default case")
                fmt.Println("block")
                time.Sleep(5 * time.Second) // simulate a long running function
                fmt.Println("unblock")
            }
        }
    }()

    fmt.Println("Goroutine A before time.Sleep()")
    time.Sleep(1 * time.Second)
    fmt.Println("Goroutine A after sleep")
    stop <- 1
    fmt.Println("Goroutine A after stop")
}

输出

Goroutine A before time.Sleep()
Goroutine B before for loop
Goroutine B inside for loop
Goroutine B default case
block
Goroutine A after sleep
unblock
Goroutine B inside for loop
return
Goroutine A after stop

有用的链接: https://gobyexample.com/timeouts

答案 2 :(得分:2)

如果你以某种方式使你的功能可以中断,你只能这样做,即你无法阻止它自己的阻塞呼叫。如果您自己编写阻止功能,通常可以设置具有多个案例和通道的选择。

您的示例看起来像

package main

import (
    "fmt"
    "time"
)

func main() {
    stop := make(chan int)

    go func() {
        for {
            fmt.Println("block")
            select {
            case <-time.After(5 * time.Second):
                fmt.Println("unblock")
            case <-stop:
                fmt.Println("stopped")
                return
            }

        }
    }()

    time.Sleep(2 * time.Second)
    stop <- 1

    // this is just to give the goroutine time to write "stopped" before we exit
    select{}
}