首先需要3个线程而不是1个线程,因为1个线程在执行的过程中是阻塞的,无法判断超时,1个线程判断是否超时,最后是1个主线程接受这两个线程发出来的信号,如果是SSH执行的线程执行结束,则现将执行结束的消息告诉主现场,主线程拿着结果返回并结束,在结束前通知判断超时是否结束的线程也结束。
根据这个思路我可以先经验技术验证,首先是最简单的模拟ssh执行的函数,传入执行时间,传入结果,其次是时间超时这个函数,不好处理,如果是传入超时时间和开始时间,返回是否超时,那么在使用他之前需要一个for循环一直遍历,看是否超时,这时候我记得Go语言里面协程之间传递消息有两种方式,一种方式是通过waitgroup死锁参数的变量,另一种方式是通过通道传递,这里面为什省事我先用通道,用通道传递现在是超时了,这时候模拟SSH也是,可以得到下面的代码
func mockSSH(resultChan chan Result, runtime time.Duration) {
time.Sleep(runtime)
resultChan <- Result{Code: 1}
}
func checkTimeOut(timeoutChan chan bool, timeout time.Duration) {
endTime := time.Now().Add(timeout)
for {
if time.Now().After(endTime){
timeoutChan <- true
break
}
time.Sleep(1*time.Second)
}
}
func main() {
timeoutChan := make(chan bool)
resultChan := make(chan Result)
go checkTimeOut(timeoutChan, 10*time.Second)
go mockSSH(resultChan, 30*time.Second)
}
然后我需要考虑主线程判断任务结束,这里用for循环,借助Go语言的select可以实现同时判断多个协程,我预期他会执行超时了,然后执行主线程执行结束,可实际结果并不是这样,是执行超时了、拿到执行结果、然后才是主线程执行结束,这个执行顺序真的好奇怪,是因为select退出机制我不理解吗,我回到官网上去看一下
timeoutChan := make(chan bool)
resultChan := make(chan Result)
go checkTimeOut(timeoutChan, 10*time.Second)
go mockSSH(resultChan, 30*time.Second)
for {
select {
case timeout := <-timeoutChan:
if timeout {
fmt.Println("超时了")
break
}
case result := <-resultChan:
fmt.Println("拿到执行结果", result)
break
}
}
fmt.Println("主线程执行结束")
官网上是select里面是return,那么我来试一下select后面跟着return,符合我一半的预期,就是执行了超时了,然后线程直接结束,看来return会让整个线程直接结束
func main() {
timeoutChan := make(chan bool)
resultChan := make(chan Result)
go checkTimeOut(timeoutChan, 10*time.Second)
go mockSSH(resultChan, 30*time.Second)
for {
select {
case timeout := <-timeoutChan:
if timeout {
fmt.Println("超时了")
return
}
case result := <-resultChan:
fmt.Println("拿到执行结果", result)
return
}
}
fmt.Println("主线程执行结束")
}
刚才和deepseek沟通,发现一个情况,我执行了超时了,其余mockSSH和主线程并没有结束,这可能是一个隐患,也许for循环这里适合放到最后,如果我是在想要后面的主线程结束,也许可以放到defer里面也许能解决问题,发现确实如此,defer里面可以解决,这样就实现了我期待的业务逻辑
type Result struct {
Code int
}
func mockSSH(resultChan chan Result, runtime time.Duration) {
time.Sleep(runtime)
resultChan <- Result{Code: 1}
}
func checkTimeOut(timeoutChan chan bool, timeout time.Duration) {
endTime := time.Now().Add(timeout)
for {
if time.Now().After(endTime) {
timeoutChan <- true
break
}
time.Sleep(1 * time.Second)
}
}
func main() {
timeoutChan := make(chan bool)
resultChan := make(chan Result)
go checkTimeOut(timeoutChan, 10*time.Second)
go mockSSH(resultChan, 30*time.Second)
defer func() {
fmt.Println("主线程执行结束")
}()
for {
select {
case timeout := <-timeoutChan:
if timeout {
fmt.Println("超时了")
return
}
case result := <-resultChan:
fmt.Println("拿到执行结果", result)
return
}
}
}
这样还有可以改进的地方,就是一个线程结束的时候,另一个线程没有结束,