user99999
user99999

Reputation: 2024

Reading from file without locking it

I constantly write to one file some data everytime requests come in. I also want to parse this file and read from it sometimes. How can I do this reading if the file is constantly being written to? What's the pattern?

Upvotes: 2

Views: 1878

Answers (2)

user6169399
user6169399

Reputation:

so you want Read & Write file without locking it,
Stateful Goroutines and channels are the solution:
one Stateful Goroutine manages the read and write requests.
working sample code:

package main

import "os"
import "fmt"
import "sync"

type Buffer struct {
    buf []byte
    off int64
}

var wg sync.WaitGroup

func main() {
    wr := make(chan *Buffer)
    rq := make(chan *Buffer)
    rd := make(chan *Buffer)
    wg.Add(1)
    go manage("file.bin", wr, rq, rd)

    wr <- &Buffer{buf: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, off: 0}            //write
    wr <- &Buffer{buf: []byte{10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, off: 10} //write

    rq <- &Buffer{buf: make([]byte, 20), off: 0} //request to read
    b := <-rd                                    //read
    fmt.Println("read:", b)
    //read: &{[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] 0}

    rq <- &Buffer{buf: nil, off: -1} // exit
    wg.Wait()
    fmt.Println("Bye") //Bye
}

func manage(filename string, wr, rq, rd chan *Buffer) {
    f, e := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
    if e != nil {
        panic(e.Error())
    }
    defer f.Close()
    for {
        select {
        case b := <-wr:
            f.WriteAt(b.buf, b.off)
        default:
        }
        select {
        case b := <-rq:
            if b.off == -1 {
                wg.Done()
                return
            }
            n, _ := f.ReadAt(b.buf, b.off)
            rd <- &Buffer{b.buf[1:n], b.off}
        default:
        }
    }
}

Upvotes: 0

noisypixy
noisypixy

Reputation: 774

You could make use of sync.RWMutex. Then:

  • When you need to read the file, call RLock(), read, then call RUnlock().
  • When you need to write to the file, call Lock(), write, then call Unlock().

As long as you do that, you're ensuring that:

  • Only one goroutine will be writing to the file at any time.
  • If you try to read the file while it's being modified, the lock will wait until you finish writing before starting to read the file.
  • If you try to write to the file while it's being read, the lock will wait until you finish reading before starting to write.

Here's a very little example:

package sample

import (
    "sync"
)

var fileMutex = new(sync.RWMutex)

func readFile() {
    fileMutex.RLock()
    defer fileMutex.RUnlock()

    // Read the file. Don't modify it.
}

func writeFile() {
    fileMutex.Lock()
    defer fileMutex.Unlock()

    // Write to the file.
}

Upvotes: 5

Related Questions