Golang中如何停止Goroutine下运行的fmt.Scanf进程
Golang中如何停止Goroutine下运行的fmt.Scanf进程
你好,我正在开发一个程序,如果用户在一段时间后没有在控制台输入任何内容,它会超时并移动到下一个进程。在设计我的程序时,我参考了这篇文章,然而,这并没有真正取消 fmt.Scanf 的进程,并且如果在超时后还有另一个 scanf,就会变得有问题。
我创建了一个修改后的代码版本,在超时后请求新的输入:
func main() {
ch := make(chan int)
go func() {
var age int
fmt.Printf("Your age: ")
fmt.Scanf("%d", &age)
fmt.Println("Age: ", age)
ch <- 1
}()
select {
case <-ch:
fmt.Println("Exiting.")
os.Exit(0)
case <-time.After(3 * time.Second):
fmt.Println("\nTimed out")
var height int
fmt.Printf("Your height: ")
fmt.Scanf("%d\n", &height)
fmt.Println("Height: ", height)
}
}
以下是一些输入示例:

如你所见,当它询问身高时,它从年龄的 scanf 获取了扫描输入,而不是身高的 scanf。这是因为年龄的 scanf 仍然处于活动状态。
现在我的问题是,如何停止第一个 scanf 的进程?或者是否有其他读取用户输入的方法,不需要等待并阻塞直到用户在控制台输入内容?
更多关于Golang中如何停止Goroutine下运行的fmt.Scanf进程的实战教程也可以访问 https://www.itying.com/category-94-b0.html
在提出问题后,我进行了更深入的探究,发现一旦调用了 scanf(或 read)函数,就无法取消其执行过程。这篇文章对此进行了非常详细的解释。
因此,我想出了一个不同的方法。不直接调用 scanf,而是使用 syscall.Select 函数来监听控制台输入:
func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error)
据我所知,这个函数本质上是在给定的超时时间内监视指定的文件描述符,看它们是否准备好进行读取或写入操作。如果可读或可写,则返回 1,否则返回 0。这是一个 UNIX 特性,可能无法在所有机器上工作,尤其是在 Windows 上。你可以在这里找到关于 Select 的更多信息。
使用 select 调用后,程序如下所示:
func main() {
fmt.Printf("Your age: ")
fd := 0 // stdin is 0
fds := syscall.FdSet{}
FD_SET(&fds, fd)
tv := syscall.Timeval{Sec: 3, Usec: 0} // 3 seconds timeout
n, err := syscall.Select(fd+1, &fds, nil, nil, &tv)
if err != nil {
fmt.Println("Error: ", err)
return
}
if n == 0 {
fmt.Println("\nTimed out")
var height int
fmt.Printf("Your height: ")
fmt.Scanf("%d", &height)
fmt.Println("Height: ", height)
} else if n == 1 {
var age int
fmt.Scanf("%d", &age)
fmt.Println("Age: ", age)
} else {
fmt.Println("Error!")
}
}
// by https://go.dev/play/p/LOd7q3aawd
func FD_SET(p *syscall.FdSet, i int) {
p.Bits[i/64] |= 1 << (uint(i) % 64)
}
按预期工作!
更多关于Golang中如何停止Goroutine下运行的fmt.Scanf进程的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


