Zih*_*Liu 2 linux multithreading posix go
当两个 Goroutine 同时写入文件时,线程安全吗os.File.Write()?
根据这个问题Is os.File's Write() threadsafe? ,它不是线程安全的。./test.txt然而,以下代码的输出文件并没有出现错误。
根据这个问题,多个进程同时写入同一个文件是否安全?[CentOs 6, ext4],POSIX“原始”IO 系统调用是线程安全的。os.File.Write()使用 POSIX IO 系统调用,那么我们可以说它是线程安全的吗?
package main
import (
"fmt"
"os"
"sync"
)
func main() {
filePath := "./test.txt"
var wg sync.WaitGroup
wg.Add(2)
worker := func(name string) {
// file, _ := os.Create(filePath)
file, _ := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE, 0666)
defer file.Close()
defer wg.Done()
for i := 0; i < 100000; i++ {
if _, err := file.Write([]byte(name + ": is os.File.Write() thread safe?\n")); err != nil {
fmt.Println(err)
}
}
}
go worker("worker1")
go worker("worker2")
wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)
小智 10
文档没有明确说明它是线程安全的。
查看 Go 1.16.5 版本源代码:
// Write implements io.Writer.
func (fd *FD) Write(buf []byte) (int, error) {
if err := fd.writeLock(); err != nil {
return 0, err
}
defer fd.writeUnlock()
...
Run Code Online (Sandbox Code Playgroud)
它使用内部同步。除非您正在编写火星着陆器的代码,否则我认为假设写入是线程安全的就可以了。
一般来说,您不应期望Write对 an 的调用io.Writer会以原子方式(即一次全部)写出。如果您不需要交错输出,建议使用更高级别的同步。
即使您可以假设由于内部锁定或因为它是单个系统调用,*os.File每个调用Write都会被原子地写出,但也不能保证使用该文件的任何内容都会这样做。例如:
fmt.Fprintf(f, "[%s] %s\n", date, message)
Run Code Online (Sandbox Code Playgroud)
该fmt库不保证这将对io.Writer. 例如,它可能先刷新[,然后是日期,然后] 是消息,最后是\n单独的,这可能会导致两条日志消息交错。
实际上,写入*os.File可能是原子的,但很难在不产生大量分配的情况下使其发挥作用,并且做出这种假设可能会损害应用程序到不同操作系统或体系结构,甚至不同环境的可移植性。例如,编译为 WASM 的二进制文件可能不会有相同的行为,或者您的二进制文件在写入 NFS 支持的文件时会有不同的行为。