为了账号安全,请及时绑定邮箱和手机立即绑定

并发写入文件

并发写入文件

Go
繁华开满天机 2021-10-04 15:37:30
在go中,如何控制并发写入文本文件?我问这个是因为我将有多个 goroutine 使用相同的文件处理程序写入文本文件。我写了这段代码来尝试看看会发生什么,但我不确定我是否“正确”做到了:package mainimport (    "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: 2out: 5Poriuntitng: 6从本质上讲,由于缺乏同步,我预计角色会出现不连贯和交织的情况。我是否没有编写代码来哄骗这种行为?或者在调用fmt.Fprintf同步写入过程中是否有某种机制?
查看完整描述

2 回答

?
慕田峪7331174

TA贡献1828条经验 获得超13个赞

控制并发访问的一种简单方法是通过服务 goroutine,从通道接收消息。该 goroutine 将拥有对该文件的唯一访问权限。因此,访问将是顺序的,没有任何竞争问题。

通道在交错请求方面做得很好。客户端写入通道而不是直接写入文件。频道上的消息会自动为您交错。

与简单地使用互斥锁相比,这种方法的好处在于您可以开始将程序视为微服务的集合。这是 CSP 方式,可以轻松地从较小的组件组合大型系统。


查看完整回答
反对 回复 2021-10-04
?
繁星coding

TA贡献1797条经验 获得超4个赞

有很多方法可以控制并发访问。最简单的方法是使用Mutex:


var mu sync.Mutex


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

    mu.Lock()

    defer mu.Unlock()

    // 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 修改同一个变量会导致奇怪的行为)


查看完整回答
反对 回复 2021-10-04
  • 2 回答
  • 0 关注
  • 223 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信