猿问

并发写入文件

在go中,如何控制并发写入文本文件?


我问这个是因为我将有多个 goroutine 使用相同的文件处理程序写入文本文件。


我写了这段代码来尝试看看会发生什么,但我不确定我是否“正确”做到了:


package main


import (

    "os"

    "sync"

    "fmt"

    "time"

    "math/rand"

    "math"

)



func WriteToFile( i int, f *os.File, w *sync.WaitGroup ){

    //sleep for either 200 or 201 milliseconds

    randSleep := int( math.Floor( 200 + ( 2 * rand.Float64() ) ) )

    fmt.Printf( "Thread %d waiting %d\n", i, randSleep )

    time.Sleep( time.Duration(randSleep) * time.Millisecond )


    //write to the file

    fmt.Fprintf( f, "Printing out: %d\n", i )

    //write to stdout

    fmt.Printf( "Printing out: %d\n", i )

    w.Done()

}


func main() {

    rand.Seed( time.Now().UnixNano() )


    d, err := os.Getwd()

    if err != nil {

        fmt.Println( err )

    }

    filename := d + "/log.txt"


    f, err := os.OpenFile( filename, os.O_CREATE | os.O_WRONLY | os.O_TRUNC, 0666 )


    if err != nil {

        fmt.Println( err )

    }

    var w *sync.WaitGroup = new(sync.WaitGroup)

    w.Add( 10 )


    //start 10 writers to the file

    for i:=1; i <= 10; i++ {

        go WriteToFile( i, f, w )

    }


    //wait for writers to finish

    w.Wait()


}

我一半期望输出会在文件中显示类似这样的内容,而不是我得到的连贯输出:


Printing Printing out: 2

out: 5

Poriuntitng: 6

从本质上讲,由于缺乏同步,我预计角色会出现不连贯和交织的情况。我是否没有编写代码来哄骗这种行为?或者在调用fmt.Fprintf同步写入过程中是否有某种机制?


繁华开满天机
浏览 224回答 2
2回答

慕田峪7331174

控制并发访问的一种简单方法是通过服务 goroutine,从通道接收消息。该 goroutine 将拥有对该文件的唯一访问权限。因此,访问将是顺序的,没有任何竞争问题。通道在交错请求方面做得很好。客户端写入通道而不是直接写入文件。频道上的消息会自动为您交错。与简单地使用互斥锁相比,这种方法的好处在于您可以开始将程序视为微服务的集合。这是 CSP 方式,可以轻松地从较小的组件组合大型系统。

繁星coding

有很多方法可以控制并发访问。最简单的方法是使用Mutex:var mu sync.Mutexfunc WriteToFile( i int, f *os.File, w *sync.WaitGroup ){&nbsp; &nbsp; mu.Lock()&nbsp; &nbsp; defer mu.Unlock()&nbsp; &nbsp; // etc...}至于为什么你没有看到问题,Go 使用操作系统调用来实现文件访问,并且这些系统调用是线程安全的(强调):根据 POSIX.1-2008/SUSv4 Section XSI 2.9.7(“与常规文件操作的线程交互”):在 POSIX.1-2008 中指定的效果中,当它们对常规文件或符号链接进行操作时,以下所有函数都应该是原子的: ...随后列出的 API 包括 write() 和 writev(2)。跨线程(和进程)应该是原子的,其中包括文件偏移的更新。但是,在 3.14 版之前的 Linux 上,情况并非如此:如果共享打开文件描述(请参阅 open(2))的两个进程同时执行 write()(或 writev(2)),则 I /O 操作在更新文件偏移量方面不是原子的,因此两个进程输出的数据块可能(错误地)重叠。 此问题已在 Linux 3.14 中修复。我仍然会使用锁,因为 Go 代码不是自动线程安全的。(两个 goroutine 修改同一个变量会导致奇怪的行为)
随时随地看视频慕课网APP

相关分类

Go
我要回答