同时处理通道会导致意外输出

时间:2015-07-09 14:05:13

标签: concurrency go

我有一个无缓冲的通道,i工作量从(文件系统路径)获取值并处理它(通过HTTP发送文件内容)。当我增加i时,我遇到了问题。

当我运行时:

paths := make(chan string)

for i := 0; i < 5; i++ {
    go func() {
        for path := range paths {
            fmt.Println(path)
        }
    }()
}

walkFn := func(path string, info os.FileInfo, err error) error {
    if !info.IsDir() {
        paths <- path
    }
    return nil
}

filepath.Walk("/tmp/foo", walkFn)
close(paths)

它可以预期运行并输出/tmp/foo的所有内容:

/tmp/foo/2
/tmp/foo/file9
/tmp/foo/file91
/tmp/foo/file90
/tmp/foo/file900
/tmp/foo/file901
/tmp/foo/file902
/tmp/foo/file92
/tmp/foo/file97
/tmp/foo/file93
/tmp/foo/file94
/tmp/foo/file95
/tmp/foo/file96
/tmp/foo/file98
/tmp/foo/file99

但是当我通过HTTP发送文件内容时,受影响文件的数量突然下降:

for i := 0; i < 5; i++ {
    go func() {
        for path := range paths {
            resp, err := http.Head("https://example.com/" + strings.TrimPrefix(path, rootDir+"/"))
            if err != nil {
                fmt.Printf("Error: %s\n", err)
                return
            }

            fmt.Printf("%s: %s\n", path, resp.Status)
        }
    }()
}

受影响的文件数量从15减少(这是目录中存在的数量),直到10:

/tmp/foo/2: 404 Not Found
/tmp/foo/file901: 404 Not Found
/tmp/foo/file900: 404 Not Found
/tmp/foo/file9: 404 Not Found
/tmp/foo/file90: 404 Not Found
/tmp/foo/file902: 404 Not Found
/tmp/foo/file91: 404 Not Found
/tmp/foo/file92: 404 Not Found
/tmp/foo/file93: 404 Not Found
/tmp/foo/file94: 404 Not Found

这是一个表格,它将i的值与输出行数相关联:

+-----+-------+
| `i` | lines |
+-----+-------+
| 1   | 15    |
| 5   | 10    |
| 6   | 9     |
| 15  | 0     |
+-----+-------+

为什么会发生这种情况?如何同时处理所有频道条目?这是http请求的问题吗?

1 个答案:

答案 0 :(得分:1)

问题是在这一行之后:

filepath.Walk("/tmp/foo", walkFn)

所有路径都是通过paths频道发送的,这意味着有人收到了这些路径。但是,这并不意味着接收goroutine的人已经完全完成了。

因此,当你的程序在close(paths)之后退出时,仍有goroutines正在运行,因为main已完成而被杀死。

https://golang.org/ref/spec#Program_execution

  

程序执行从初始化主包然后调用main函数开始。当该函数调用返回时,程序退出。 它不会等待其他(非主要)goroutines完成。

一个简单的解决方案是添加

select{}

在您的计划结束时。这将使它永远阻止。