为什么这段代码没有达到竞争条件?
Why isn't this code hitting a race condition?
我有这个 go 代码,它遍历目录文件树并生成其中每个文件的 MD5 哈希值,并将结果写入输出文件。
package main
import (
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"sync"
)
func main() {
filePath := os.Args[1]
output := os.Args[2]
wg := &sync.WaitGroup{}
err := filepath.Walk(filePath, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
wg.Add(1)
go func(path string) {
md5Sum, _ := md5File(path)
if err := writeToFile(path, md5Sum, output); err != nil {
panic(err)
}
wg.Done()
}(path)
}
return nil
})
if err != nil {
panic(err)
}
wg.Wait()
}
func md5File(filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", err
}
defer file.Close()
hash := md5.New()
if _, err := io.Copy(hash, file); err != nil {
return "", err
}
checksum := hash.Sum(nil)
return string(hex.EncodeToString(checksum)), nil
}
func writeToFile(filePath, md5sum, output string) error {
file, err := os.OpenFile(output, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
if err != nil {
return err
}
defer file.Close()
file.WriteString(fmt.Sprintf("%s %s\n", md5sum, filePath))
return file.Sync()
}
根据我的理解,它在写入输出文件时一定会 运行 进入竞争状态,但它永远不会。我的意思是我已经无数次执行此代码,从未遇到过任何问题。它甚至每次都产生相同的结果。
这是为什么?我错过了什么吗?
更新: 当我说它必然会面临竞争条件时,我的意思是当 运行 设置多个 goroutine 时,可能有多个 goroutine 想要同时写入文件。
When I say it is bound to face a race condition I mean when running multiple goroutines it is possible for more than one goroutine to want to write to file at the same time.
多次打开一个文件不是问题。而且由于您明确使用 O_APPEND
,因此写入不会相互影响。引用
来自 man open:
O_APPEND ...
Before each write(2), the file offset is positioned at the end of the file, as if with lseek(2). The modification of the file offset and the write operation are performed as a single atomic step.
我有这个 go 代码,它遍历目录文件树并生成其中每个文件的 MD5 哈希值,并将结果写入输出文件。
package main
import (
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"sync"
)
func main() {
filePath := os.Args[1]
output := os.Args[2]
wg := &sync.WaitGroup{}
err := filepath.Walk(filePath, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
wg.Add(1)
go func(path string) {
md5Sum, _ := md5File(path)
if err := writeToFile(path, md5Sum, output); err != nil {
panic(err)
}
wg.Done()
}(path)
}
return nil
})
if err != nil {
panic(err)
}
wg.Wait()
}
func md5File(filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", err
}
defer file.Close()
hash := md5.New()
if _, err := io.Copy(hash, file); err != nil {
return "", err
}
checksum := hash.Sum(nil)
return string(hex.EncodeToString(checksum)), nil
}
func writeToFile(filePath, md5sum, output string) error {
file, err := os.OpenFile(output, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
if err != nil {
return err
}
defer file.Close()
file.WriteString(fmt.Sprintf("%s %s\n", md5sum, filePath))
return file.Sync()
}
根据我的理解,它在写入输出文件时一定会 运行 进入竞争状态,但它永远不会。我的意思是我已经无数次执行此代码,从未遇到过任何问题。它甚至每次都产生相同的结果。
这是为什么?我错过了什么吗?
更新: 当我说它必然会面临竞争条件时,我的意思是当 运行 设置多个 goroutine 时,可能有多个 goroutine 想要同时写入文件。
When I say it is bound to face a race condition I mean when running multiple goroutines it is possible for more than one goroutine to want to write to file at the same time.
多次打开一个文件不是问题。而且由于您明确使用 O_APPEND
,因此写入不会相互影响。引用
来自 man open:
O_APPEND ...
Before each write(2), the file offset is positioned at the end of the file, as if with lseek(2). The modification of the file offset and the write operation are performed as a single atomic step.