How to make reading and writing to file concurent in Golang?

huangapple go评论83阅读模式
英文:

How to make reading and writing to file concurent in Golang?

问题

我设置了一个Web服务器,并使用自己的包进行一些文件的读写操作。当服务器接收到TCP连接时,我会为每个连接启动一个不同的goroutine来处理请求。在请求处理函数中,我调用了some_package中的func DoSomething()

以下是web_server.go的代码:

package main

import (
	"./some_package"
	"log"
	"net"
	"net/http"
	"os"
)

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()
	some_package.DoSomething()
}

DoSomething()函数涉及文件的读写操作。你可以在some_package包中看到它的代码:

package some_package

import (
	"io/ioutil"
	"log"
	"os"
	"strconv"
)

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
}

在这种情况下,你可以使用互斥锁(mutex.Lockmutex.Unlock)来实现读写文件的并发控制,以确保一个goroutine在写入文件成功之前可以阻止其他goroutine进行读取。

以下是修改后的代码示例:

package main

import (
	"./some_package"
	"log"
	"net"
	"net/http"
	"os"
	"sync"
)

var mutex = &sync.Mutex{}

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()
	mutex.Lock()
	defer mutex.Unlock()
	some_package.DoSomething()
}

通过在handlerFunction函数中使用互斥锁,你可以确保在写入文件期间其他goroutine无法读取文件。这样可以实现对文件读写的并发控制。

希望对你有帮助!

英文:

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

答案1

得分: 8

你无法使文件的读写并发(虽然有可能,但不符合你描述的访问模式)。使用一个单独的互斥锁来序列化对文件的所有访问:

var fileMutex sync.Mutex

func DoSomething() {
    fileMutex.Lock()
    defer fileMutex.Unlock()
    
    //...
}
英文:

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()
	
	//...
}

huangapple
  • 本文由 发表于 2015年3月5日 00:25:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/28859619.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定