高朗IO比赛条件

问题描述 投票:0回答:1

提供一个非常简单的示例(我仍然不确定在任何ENV上都可以完全重现) 所以有一个插座管

func SocketPair() (*os.File, *os.File, error) {
    fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
    if err != nil {
        return nil, nil, err
    }

    f0 := os.NewFile(uintptr(fds[0]), "socket-0")
    f1 := os.NewFile(uintptr(fds[1]), "socket-1")

    return f0, f1, nil
}

和简单的CMD调用

func main() {
    f0, f1, err := utils.SocketPair()

    if err != nil {
        return panic(err)
    }

    cmd := exec.CommandContext(ctx, "cat")

    cmd.Stdin = f0
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    // pipe routine
    go func() {
        size, err := io.Copy(f1, os.Stdin)
        fmt.Printf("------res %d, %v", size, err)
        f1.Close()
    }()


    err := cmd.Run()

    if err != nil {
        return panic(err)
    }

}
so用类似的东西打电话给这个

echo "abc\ndef\nghi" | app

采用类似于

------res 11, <nil> abc def ghi

悬挂。实际上,它说
pipe routine

成功地将STDIN数据传递到了插座对。然而,CMD输入仍然没有EOF。

对于这个确切的简单示例,该问题(在我的env中)可以通过两个选项解决。
put put

pipe routine

cmd := exec.
    线之前
  • 保持在最初的位置,而是等待进入执行,如下
    pipe routine
    因此,这两个解决方案都解决了问题,并且应用程序优雅地退出。
    
  • 在更复杂的案例中,GOS例程链甚至无济于事。而是简单的调用
  • started := make(chan byte) go func() { close(started) size, err := io.Copy(f1, os.Stdin) fmt.Printf("------res %d, %v", size, err) f1.Close() }() <-started
    在工作之前就在
    time.Sleep(time.Second)
  • 之前。
看起来很像开始阅读的那一刻很重要。 so解决我不想玩的问题
cmd.Run()
在这里搜索最佳间隔(如果这确实是一个竞赛条件,这是一个坏主意)

我的关键问题在这里:这种行为的根本原因是什么。谁首先开始阅读到底有什么问题。 thanks

我猜想,您看到的悬挂问题是在设置管道的读数和写作何时设置的情况之间。这很棘手,因为有时它可以工作,有时不行!

在这里,您的两个解决方案都可以使用:

先前介绍管道例程:这使Goroutine有时间在

io.Copy / cmd.Run

尝试阅读之前启动。

使用通道同步:

启动:= make(chan byte) go func(){ 关闭(开始) //您的复制代码 }()

go race-condition goroutine stdio
1个回答
0
投票

这确保您的goroutine实际上在继续前进。 而不是使用

time.Sleep

,例如:

    cmd.Run()
  1. 根问题是,我们需要确保在命令开始尝试从中读取该管道之前准备处理管道。

    	

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.