首页  >  文章  >  后端开发  >  在 Go 中写入“stdout”真的是线程安全的吗?

在 Go 中写入“stdout”真的是线程安全的吗?

Susan Sarandon
Susan Sarandon原创
2024-10-27 07:10:02412浏览

 Is Writing to `stdout` in Go Truly Thread-Safe?

stdout 上的并发写入线程安全吗?

这个问题涉及并发写入标准输出(stdout)流的线程安全性去。具体来说,考虑以下代码:

<code class="go">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)
}</code>

尽管没有任何明显的数据竞争,但关于此代码的线程安全性仍存在一些争论。为了澄清这个问题,让我们深入研究写入 stdout 的机制并检查建议和参考。

FMT 包和 IO.Writer

fmt 包函数,例如 fmt.Fprintf,接受实现 io.Writer 接口的参数。它们在内部调用此接口的 Write 方法。

操作系统和对 stdout 的并发访问

os.Stdout 是 io.Writer 接口的实现,它将输出定向到系统的标准输出。当多个 goroutine 并发写入 os.Stdout 时,该操作的实际语义将委托给底层操作系统。

在 POSIX 系统中,使用 write(2) 系统调用。 POSIX 指定并发 write(2) 调用对于常规文件和符号链接是原子的。但是,此保证不适用于其他文件类型或非 POSIX 系统。

Go 标准库和包装器

Go 标准库提供了用于写入的包装器文件描述符和套接字。这些包装器实现 io.Writer 接口,并且在 os.Stdout 的情况下,将操作重定向到相关的系统调用。

Go 运行时确保这些包装器对于并发访问在内部是安全的。然而,它们只是将写入转发到底层操作系统,因此并发语义最终由操作系统决定。

并发 stdout 写入的影响

  • 并发调用 fmt.Fprintf 写入 os.Stdout 在 Go 运行时内并发使用是安全的。
  • 输出的实际顺序可能是不确定的,取决于操作系统、Go 运行时、

建议

  • 如果并发写入 stdout 的输出顺序至关重要,请考虑使用同步机制来强制执行特定的顺序
  • 日志包提供了一个日志框架,其中包括通过可控时间戳和日志头将线程安全日志记录到 stdout(或其他目的地)。

参考文献

  • POSIX write(2) 系统调用:https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html
  • Go 中的 io.Writer 接口: https://pkg.go.dev/io#Writer
  • Go 中的 Fmt 包:https://pkg.go.dev/fmt
  • Go 中的日志包:https:// pkg.go.dev/log

以上是在 Go 中写入“stdout”真的是线程安全的吗?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn