英文:
Is os.File.Write() thread safe in golang?
问题
当两个Goroutine同时使用os.File.Write()
写入文件时,它是否是线程安全的?
根据这个问题Is os.File's Write() threadsafe?,它不是线程安全的。然而,以下代码的输出文件./test.txt
没有出现错误。
而根据这个问题Safe to have multiple processes writing to the same file at the same time? [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()
}
英文:
Is it thread safe, when two Goroutines writes to file concurrently by os.File.Write()
?
According to this question Is os.File's Write() threadsafe?, it isn't thread safe. However, the output file ./test.txt
of the following code didn't occur errors.
And according to this question Safe to have multiple processes writing to the same file at the same time? [CentOs 6, ext4], the POSIX "raw" IO syscalls are thread safe. os.File.Write()
uses the POSIX IO syscalls, so can we say it is thread safe?
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()
}
答案1
得分: 9
文档没有明确说明它是线程安全的。
不过,从 Go 1.16.5 版本的源代码来看:
// Write 实现了 io.Writer 接口。
func (fd *FD) Write(buf []byte) (int, error) {
if err := fd.writeLock(); err != nil {
return 0, err
}
defer fd.writeUnlock()
...
它使用了内部同步。除非你在编写火星着陆器,否则我认为可以假设写操作是线程安全的。
英文:
Documentation does not explicitly say it is thread safe.
Looking at Go 1.16.5 version source code though:
// Write implements io.Writer.
func (fd *FD) Write(buf []byte) (int, error) {
if err := fd.writeLock(); err != nil {
return 0, err
}
defer fd.writeUnlock()
...
It uses internal synchronization. Unless you're coding a mars lander I'd say it's fine to assume writes are thread safe.
答案2
得分: 5
一般情况下,不应该期望对io.Writer
的Write
调用是原子性的,即一次性写入所有内容。如果你不希望输出交错,建议在更高的层次上进行同步。
即使对于*os.File
,你可以假设每次Write
调用都是原子性的,因为内部有锁定或者是一个单独的系统调用,但并不能保证使用文件的其他部分也是这样的。例如:
fmt.Fprintf(f, "[%s] %s\n", date, message)
fmt
库并不保证这将对io.Writer
进行一次性的调用。它可能会分别刷新[
, 然后是日期,然后是]
,然后是消息,最后是\n
,这可能导致两条日志消息交错。
实际上,对于*os.File
的写入可能是原子的,但要使其有用而不引起大量分配是困难的,而且假设这一点可能会影响你的应用程序在不同操作系统或架构,甚至不同环境中的可移植性。例如,你的编译为WASM的二进制文件可能没有相同的行为,或者当你将其写入NFS支持的文件时,行为可能会有所不同。
英文:
In general, you should not expect that Write
calls to an io.Writer
will be written out atomically, i.e. all at once. Synchronization at a higher level is recommended if you don't want interleaved outputs.
Even if you can assume that for *os.File
each call to Write
will be written out atomically because of either internal locking or because it's a single system call, there is no guarantee that whatever is using the file will do so. For example:
fmt.Fprintf(f, "[%s] %s\n", date, message)
The fmt
library does not guarantee that this will make a single call to the io.Writer
. It may, for example, flush [
, then the date, then ]
then the message, and then \n
separately, which could result in two log messages being interleaved.
Practically speaking, writes to *os.File
will probably be atomic, but it is difficult to arrange for this to be useful without incurring significant allocations, and making this assumption might compromise the portability of your application to different operating systems or architectures, or even different environments. It is possible, for example, that your binary compiled to WASM will not have the same behavior, or that your binary when writing to an NFS-backed file will behave differently.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论