2

I setup a webserver and I use my own package where I do some write/read from and to files. When the server gets a tcp connection, I start a different goroutine to handle the request for each connection. In the request handler func, I call the func DoSomething() of some_package.

Here's the code for web_server.go:

 package main
 import (
    sp "./some_package"
    "log"
    "net"
    "os"
    "net/http"
   )

func main() {

  l, err := net.Listen("tcp", "0.0.0.0" + ":" + "4567")
  if err != nil {
    log.Println("Error listening:", err.Error())
    os.Exit(1)
  }
  defer l.Close()
  log.Println("Listening on 0.0.0.0:4567")  
  go func() {
    for {
        // Listen for an incoming connection.
        conn, err := l.Accept()
        if err != nil {
            log.Println("Error accepting: ", err.Error())
            os.Exit(1)
        }
        // Handle connections in a new goroutine.
        go handlerFunction(conn)
    }
  }()

  log.Printf("Setting up the Webserver...")
  err = http.ListenAndServe("0.0.0.0:"+"4568", nil)
  if err != nil {
    log.Fatal(err)
  }
}

func handlerFunction(conn net.Conn) {
  defer conn.Close()
  sp.DoSomething()
}

The function DoSomething() reads and writes to file. You can see the code where it is declared in the package:

package some_package
import (    
    "io/ioutil"
    "strconv"
    "os"
    "log"
   )
func IncrementValue(pastValue string)(newValue string){
  newValueInt, _ := strconv.Atoi(pastValue)
  return strconv.Itoa(newValueInt + 1)
}

func DoSomething() (err error){
  initialValue := "1"
  filename := "myFile.txt"
  if _, err := os.Stat(filename); err == nil {
    someText, err := ioutil.ReadFile(filename)
    if err != nil {
        log.Printf("Error reading")
        return err
    }
    newValue := IncrementValue(string(someText))

    err = ioutil.WriteFile(filename,[]byte(newValue), 0644)
    if err != nil { 
        return err
    }
  }else{
    err = ioutil.WriteFile(filename,[]byte(initialValue), 0644)
    if err != nil { 
        return err
    }
  }
  return
 }

How can I use a locking mechanism like mutex.Lock and mutex.Unlock in this case to make the reading and writing to file concurrent so when one routine which is currently writing can stop the other from reading till the first one writes to file successfully?

Is my example suitable to be concurrent when reading or writing to file?

Is this the right approach to do so? Thank You

Rick-777
  • 8,290
  • 5
  • 31
  • 47
kingSlayer
  • 879
  • 1
  • 9
  • 19

1 Answers1

8

You can't make the reading and writing of a file concurrent (well, it's possible, but not with the access pattern you're describing). Use a single mutex to serialize all access to your file:

var fileMutex sync.Mutex

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

    //...
}
JimB
  • 87,033
  • 9
  • 198
  • 196
  • Or, instead of a mutex use a single goroutine in a loop that reads from a channel telling it when to modify the file and, if necessary, a channel to respond with new values/error. If you know the file isn't supposed to be modified outside of your program then you can avoid re-reading the file all time. – Dave C Mar 05 '15 at 19:53
  • while true, when protecting a single resource from multiple access, a Mutex or a RWMutex is still often the simplest choice. Yes, the file handling could be improved (outside the scope of this question), but I'll usually prefer these 3 lines of code over multiple channels, selects, and a goroutine. – JimB Mar 05 '15 at 20:07