2 回答

TA貢獻1801條經驗 獲得超16個贊
沒有辦法從該 goroutine外部的任何代碼中強制拆除該 goroutine。
因此,真正中斷處理的唯一方法是定期檢查客戶端是否消失(或者是否有另一個信號來停止處理)。
基本上這相當于構建你的處理程序是這樣的
func ReallyLongFunction(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
done := r.Context().Done()
// Check wheteher we're done
// do some small piece of stuff
// check whether we're done
// do another small piece of stuff
// …rinse, repeat
})
現在,檢查是否有內容寫入通道但不阻塞操作的方法是使用“使用默認值選擇”習慣用法:
select {
case <- done:
// We're done
default:
}
當且僅當done被寫入或被關閉(上下文就是這種情況)時,此狀態才會執行“// We're done”塊中的代碼,否則default執行分支中的空塊。
所以我們可以將其重構為類似的東西
func ReallyLongFunction(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
done := r.Context().Done()
closed := func () bool {
select {
case <- done:
return true
default:
return false
}
}
if closed() {
return
}
// do some small piece of stuff
if closed() {
return
}
// do another small piece of stuff
// …rinse, repeat
})
停止在 HTTP 處理程序中啟動的外部進程
為了解決OP的評論......
該os/exec.Cmd
類型具有Process
字段,該字段具有類型os.Process
并且該類型支持Kill
強制關閉正在運行的進程的方法。
唯一的問題是exec.Cmd.Run
阻塞直到進程退出,因此正在執行的 goroutine 無法執行其他代碼,并且如果exec.Cmd.Run
在 HTTP 處理程序中調用,則無法取消它。
如何最好地處理以這種異步方式運行程序在很大程度上取決于進程本身的組織方式,但我會這樣滾動:
在處理程序中,準備進程,然后使用
exec.Cmd.Start
(而不是Run
)啟動它。檢查已返回的錯誤值
Start
:如果是,則nil
進程已成功啟動。否則,以某種方式將失敗傳達給客戶端并退出處理程序。一旦知道流程已經啟動,該
exec.Cmd
值的一些字段就會填充與流程相關的信息;特別感興趣的是Process
類型為 的字段os.Process
:該類型具有Kill
可用于強制關閉進程的方法。啟動一個 goroutine 并向其傳遞該
exec.Cmd
值和某種合適類型的通道(見下文)。該 Goroutine 應該調用
Wait
它,一旦返回,它應該通過該通道將該事實傳達回原始 Goroutine。到底要通信什么,是一個懸而未決的問題,因為它取決于您是否想要收集進程寫入其標準輸出和錯誤流的內容和/或可能是與進程活動相關的一些其他數據。
發送數據后,該 goroutine 退出。
主 goroutine(執行處理程序)應該
exec.Cmd.Process.Kill
在檢測到處理程序應該終止時調用。終止進程最終會解除對 goroutine 的阻塞,該 goroutine在進程退出時
Wait
以相同的值執行。exec.Cmd
終止進程后,處理程序 goroutine 會在通道上等待監聽該進程的 goroutine 的回復。處理程序對該數據執行某些操作(可能是記錄它或其他什么)并退出。

TA貢獻1810條經驗 獲得超4個贊
您應該從內部取消 goroutine,因此對于長時間的計算任務,您可以提供檢查點,以停止并檢查取消:
這是服務器的測試代碼,該代碼具有例如長計算任務和取消檢查點:
package main
import (
? ? "fmt"
? ? "io"
? ? "log"
? ? "net/http"
? ? "time"
)
func main() {
? ? http.HandleFunc(`/`, func(w http.ResponseWriter, r *http.Request) {
? ? ? ? ctx := r.Context()
? ? ? ? log.Println("wait a couple of seconds ...")
? ? ? ? for i := 0; i < 10; i++ { // long calculation
? ? ? ? ? ? select {
? ? ? ? ? ? case <-ctx.Done():
? ? ? ? ? ? ? ? log.Println("Client closed the connection:", ctx.Err())
? ? ? ? ? ? ? ? return
? ? ? ? ? ? default:
? ? ? ? ? ? ? ? fmt.Print(".")
? ? ? ? ? ? ? ? time.Sleep(200 * time.Millisecond) // long calculation
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? io.WriteString(w, `Hi`)
? ? ? ? log.Println("Done.")
? ? })
? ? log.Println(http.ListenAndServe(":8081", nil))
}
這是超時的客戶端代碼:
package main
import (
? ? "io/ioutil"
? ? "log"
? ? "net/http"
? ? "time"
)
func main() {
? ? log.Println("HTTP GET")
? ? client := &http.Client{
? ? ? ? Timeout: 1 * time.Second,
? ? }
? ? r, err := client.Get(`http://127.0.0.1:8081/`)
? ? if err != nil {
? ? ? ? log.Fatal(err)
? ? }
? ? defer r.Body.Close()
? ? bs, err := ioutil.ReadAll(r.Body)
? ? if err != nil {
? ? ? ? log.Fatal(err)
? ? }
? ? log.Println("HTTP Done.")
? ? log.Println(string(bs))
}
您可以使用普通瀏覽器檢查是否取消,或者關閉、刷新、斷開連接,或者...,取消。
- 2 回答
- 0 關注
- 166 瀏覽
添加回答
舉報