App Engine Go:如何杀死正在运行的goroutine

时间:2018-04-03 09:16:52

标签: google-app-engine go goroutine google-app-engine-go

我想在App Engine中并行运行2个goroutine,这样当第一个goroutine完成它的工作时,处理程序不需要等待第二个goroutine - 它会停止secend goroutine并将结果返回给客户端。这可能吗?我尝试使用context.WithCancel(),但它没有工作(我使用go1.6)。

这是我的代码:

package mytest

import (
    "net/http"
    "sync"
    "time"

    "golang.org/x/net/context"
    "google.golang.org/appengine"
    "google.golang.org/appengine/log"
    "google.golang.org/appengine/urlfetch"
)

func init() {
    http.HandleFunc("/test", handlerTest)
    http.HandleFunc("/testwait10s", handlerTest10s)
    http.HandleFunc("/testwait5s", handlerTest5s)
}

func handlerTest(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    ctx, _ := context.WithTimeout(c, 30*time.Second)

    ctx1, ctx1Cancel := context.WithCancel(ctx)
    ctx2, ctx2Cancel := context.WithCancel(ctx)

    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        defer wg.Done()
        log.Infof(ctx1, "Go1 begin ...")
        client1 := urlfetch.Client(ctx1)
        _, err := client1.Get("http://APP_NAME.appspot.com/testwait5s")
        if err != nil {
            log.Errorf(ctx1, "Go1 failed:  %v", err)
        }
        ctx2Cancel()
        log.Infof(ctx1, "Go1 over ...")
    }()

    go func() {
        defer wg.Done()
        log.Infof(ctx2, "Go2 begin ...")
        client2 := urlfetch.Client(ctx2)
        _, err := client2.Get("http://APP_NAME.appspot.com/testwait10s")
        if err != nil {
            log.Errorf(ctx2, "Go2 failed %v", err)
        }
        ctx1Cancel()
        log.Infof(ctx2, "Go2 over ...")
    }()

    wg.Wait()
    log.Infof(ctx1, "Go1 and GO2 over")
}

func handlerTest10s(w http.ResponseWriter, r *http.Request) {
    time.Sleep(10 * time.Second)
    return
}

func handlerTest5s(w http.ResponseWriter, r *http.Request) {
    time.Sleep(5 * time.Second)
    return
}

有什么想法吗?谢谢!

2 个答案:

答案 0 :(得分:1)

只需创建一个通知通道,然后向其发送一个计算结束的信号,您就可以继续而无需等待另一个。

func handlerTest(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    ctx, cancel := context.WithTimeout(c, 30*time.Second)
    done := make(chan error, 2)

    work := func(url, name string) {
        log.Infof(ctx, "%s begin ...", name)
        client := urlfetch.Client(ctx)
        req, err := http.NewRequest(http.MethodGet, url, nil)
        if err != nil {
            log.Errorf(ctx, "%s failed:  %v", name, err)
            done <- err
            return
        }
        req = req.WithContext(ctx)
        _, err = client.Do(req)
        done <- err
        if err != nil {
            log.Errorf(ctx, "%s failed:  %v", name, err)
            return
        }
        cancel()
        log.Infof(ctx, "%s over ...", name)
    }
    go work("go1", "http://APP_NAME.appspot.com/testwait5s")
    go work("go2", "http://APP_NAME.appspot.com/testwait10s")

    for i := 0; i < cap(done); i++ {
        if err := <-done; err == nil {
            log.Infof(ctx, "executed without errors")
            return
        }
    }
    log.Error(ctx, "both computations have failed")
}

答案 1 :(得分:0)

您可以尝试将wg.Add()的值减少到wg.Add(1)而不是wg.Add(2)

当一个例程完成后,wg.Done()会将计数器值减少1。因此,在这种情况下,WaitGroup (wg)计数器值将变为ZERO。因此,最后一行wg.Wait()将不会等待其他常规例程完成。

请注意,如果wg计数器的值低于零,则会使剩余的例行程序发生混乱。因此,有条不紊地退出常规程序。