下面的代码不会引发数据争夺
package main
import (
"fmt"
"os"
"strings"
)
func main() {
x := strings.Repeat(" ", 1024)
go func() {
for {
fmt.Fprintf(os.Stdout, x+"aa\n")
}
}()
go func() {
for {
fmt.Fprintf(os.Stdout, x+"bb\n")
}
}()
go func() {
for {
fmt.Fprintf(os.Stdout, x+"cc\n")
}
}()
go func() {
for {
fmt.Fprintf(os.Stdout, x+"dd\n")
}
}()
<-make(chan bool)
}
我尝试了多种长度的数据,使用了https://play.golang.org/p/29Cnwqj5K30变体This post说不是TS。
This mail不能真正回答问题,或者我听不懂。
os和fmt的软件包文档对此没有太多提及。我承认我没有挖掘这两个软件包的源代码来寻找进一步的解释,它们对我来说似乎太复杂了。
有哪些建议及其引用?
最佳答案
我不确定它是否可以作为一个明确的答案,但我将尝试提供一些见解。F*
包的fmt
-函数仅声明它们采用实现io.Writer
接口(interface)的类型的值并在其上调用Write
。
这些函数本身可以安全地并发使用-从某种意义上说,可以并发调用任意数量的fmt.Fwhaveter
是可以的:程序包本身已经为此做好了准备,
但是在Go中支持接口(interface)并没有说明任何关于实类型并发方面的信息。
换句话说,将允许或不允许并发的实际点推迟到fmt
函数写入的“写入器”。
(还应该记住,与库存软件包fmt.*Print*
所提供的功能相反,允许Write
函数在其目的地多次调用log
。)
因此,我们基本上有两种情况:
io.Writer
的定制实现。 *os.File
或net
包功能产生的套接字周围的包装。 第一种情况很简单:无论实现者做什么。
第二种情况更难:据我所知,Go标准库对此的立场(尽管在文档中没有明确说明)是因为它提供了围绕OS提供的“事物”(例如文件描述符和套接字)的包装是合理的。 “薄”及其所实现的任何语义都由在特定系统上运行的stdlib代码可传递地实现。
例如,当POSIX requires that
write(2)
calls are atomic with regard to one another在常规文件或符号链接(symbolic link)上运行时。这意味着,由于在包装文件描述符或套接字的事物上对Write
的任何调用实际上都会导致tagret系统的单个“写入”系统调用,因此您可以查阅目标操作系统的文档并了解将要发生的事情。请注意,POSIX仅告诉文件系统对象,并且如果
os.Stdout
是打开到终端(或伪终端),管道还是支持write(2)
syscall的其他任何东西,则结果将取决于相关子系统和/或驱动程序实现—例如,来自多个并发调用的数据可能会散布,或者其中一个调用或两者都可能会被OS失败—不太可能,但仍然如此。回到Go,从我收集的数据来看,关于封装文件描述符和套接字的Go stdlib类型的以下事实适用:
Write
和Read
调用“一对一”映射到基础对象-也就是说,Write
调用永远不会拆分为两个或多个基础syscall,并且Read
调用从不从多个结果中“粘贴”底层系统调用。(顺便说一句,人们偶尔会被这种节俭的行为绊倒,例如,以this或this为例。)
因此,基本上,当我们考虑到
fmt.*Print*
可以在每次调用中自由调用Write
多次的事实时,使用os.Stdout
的示例将:os.Stdout
分配了一些自定义实现,否则TL; DR
fmt.Fprint*
的多个并发调用将写入同一“writer”值,从而将它们的并发性推迟到“writer”的实现(类型)。 基本上,在您的情况下,如果您需要确保对
fmt.Fprint*
的任何特定调用所产生的数据作为单个连续片段输出到操作系统提供的实际数据接收器中,则您需要对这些调用进行序列化,因为fmt
包不提供关于提供的“写入器”导出功能的Write
调用次数的保证。序列化可以是外部的(显式的,即“获取一个锁,调用
fmt.Fprint*
,释放该锁”),也可以是内部的-通过将os.Stdout
包装在可以管理锁的自定义类型中并使用它来进行)。而在使用
log
包时,它就可以做到这一点,并且可以直接用作它提供的“记录器”(包括默认记录器),从而禁止输出“日志头”(例如时间戳和文件名)。
关于go - 在stdout线程上并发写入是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65214079/