我在這裏遇到問題,無法安全退出goroutine。如何停止由進程啓動的外部I/O阻塞的goroutine?
我在使用exec.Command創建一個外部進程(存儲CMD,標準輸入管和該方法的標準輸出管):
exec.Command(args[0], args[1]...) // args[0] is a base command
每當有必要啓動過程我打電話:
cmd.Start()
然後在啓動和附加我跑2個夠程:
shutdown := make(chan struct{})
// Run the routine which will read from process and send the data to CmdIn channel
go pr.cmdInRoutine()
// Run the routine which will read from CmdOut and write to process
go pr.cmdOutRoutine()
cmdInRo utine:
func (pr *ExternalProcess) cmdInRoutine() {
app.At(te, "cmdInRoutine")
for {
println("CMDINROUTINE")
select {
case <-pr.shutdown:
println("!!! Shutting cmdInRoutine down !!!")
return
default:
println("Inside the for loop of the CmdInRoutine")
if pr.stdOutPipe == nil {
println("!!! Standard output pipe is nil. Sending Exit Request !!!")
pr.ProcessExit <- true
close(pr.shutdown)
return
}
buf := make([]byte, 2048)
size, err := pr.stdOutPipe.Read(buf)
if err != nil {
println("!!! Sending exit request from cmdInRoutine !!!")
pr.ProcessExit <- true
close(pr.shutdown)
return
}
println("--- Received data for sending to CmdIn:", string(buf[:size]))
pr.CmdIn <- buf[:size]
}
}
}
cmdOutRoutine:
func (pr *ExternalProcess) cmdOutRoutine() {
app.At(te, "cmdOutRoutine")
for {
select {
case <-pr.shutdown:
println("!!! Shutting cmdOutRoutine down !!!")
return
case data := <-pr.CmdOut:
println("Received data for sending to Process: ", data)
if pr.stdInPipe == nil {
println("!!! Standard input pipe is nil. Sending Exit Request !!!")
pr.ProcessExit <- true
return
}
println("--- Received input to write to external process:", string(data))
_, err := pr.stdInPipe.Write(append(data, '\n'))
if err != nil {
println("!!! Couldn't Write To the std in pipe of the process !!!")
pr.ProcessExit <- true
return
}
}
}
}
有趣的情況在這裏:
1)當進程發送EOF(不介意pr.ProcessExit < - 真我被通知到父處理程序使用通道來停止並退出進程)cmdInRoutine我也關閉了關閉通道,這讓cmdOutRouti ne退出,因爲在select語句中不存在默認情況,所以它會阻止並等待退出或等待使用存儲的stdInPipe寫入正在運行的進程的數據。
2)當我想停止goroutines,但離開進程運行,即暫停讀取和寫入我正在關閉關閉通道,希望這2個goroutines將結束。
- cmdOutRoutine打印!!!關閉cmdOutRoutine!因爲選擇不具有默認情況下並關閉關閉通道使返回幾乎立即
- cmdOutRoutine沒有打印任何東西,我有一種奇怪的感覺,它甚至沒有回來,我想是因爲它擋在默認情況下從stdInPipe讀取。
我想之前在for循環運行cmdOutRoutine內的另一個夠程,並有標準輸入轉換成通道的過程數據,那麼我將能夠在消除默認情況下cmdInRoutine但是這創造了另一個問題,新的goroutine也必須停止,它仍然會被正在運行的進程的stdIn中的讀取屏蔽。
任何想法如何解決這個問題(修改邏輯)以滿足隨時關閉和啓動goroutines(進程I/O)而不是運行進程本身的需求?或者有沒有辦法避免阻止讀和寫的調用,我還沒有意識到?
非常感謝。
你有兩個獨立的程序運行時,你應該使用兩個獨立的關斷通道。只有像你這樣做的人,你不知道哪一個會首先從通道收集關閉信號,而另一個則會阻塞。 – RayfenWindspear
另請注意,命令運行後無法重新使用https://golang.org/pkg/os/exec/#Cmd – RayfenWindspear
@RayfenWindspear可以使用單個關閉通道。從關閉的通道讀取將立即返回零值,因此兩個goroutine都會收集關閉信號。 – user1431317