golang 中 os.File.Write() 线程安全吗?

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)

它使用内部同步。除非您正在编写火星着陆器的代码,否则我认为假设写入是线程安全的就可以了。


Kyl*_*ons 5

一般来说,您不应期望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 支持的文件时会有不同的行为。