【问题标题】:Synchronization in a Go-lang webapplicationGolang Web 应用程序中的同步
【发布时间】:2013-02-08 19:16:01
【问题描述】:

我正在编写一个 golang 网络应用程序。 Web 应用程序访问文件系统(读取和写入)和 sqlite3 数据库文件。

问题 1:如何在 Go 中同步文件系统访问?

type DataObject struct {
  data []byte
}

func (*d DataObject) Write() {
   //
   // Synchronization ?
   //
   ioutil.WriteFile("file.name", d.data, 0644)
   //
   // Stop synchronization ?
   //
}

问题2:需要同步sqlite3数据库文件访问吗?

type SqlObject struct {
  sqldata string
}

func (*s SqlObject) Store() error {
  //
  // Open the file, do I need some sort of synchronization?
  //
  con, err := sqlite.Open("database/datafile.db")
  if err != nil {
    return err
  }
  defer con.Close()

  err = con.Exec("INSERT INTO data(sqldata) values(?)", s.sqldata)
  if err != nil {
    return err
  }
  return nil
}

我正在使用 gosqlite3 驱动程序 (http://code.google.com/p/gosqlite/)。

【问题讨论】:

    标签: web-applications concurrency sqlite synchronization go


    【解决方案1】:

    对于文件,这取决于您的应用程序。如果您只有一个 goroutine 写入文件,则不需要。如果不止一个,则取决于:

    如果你在不同的进程(程序)之间进行协调,你可以使用flock(它可能不会很有趣)。

    如果您在程序中协调多个 goroutine,您可以使用 mutexes 或者您可以查看是否可以重新组织程序,以便只有一个例程写入文件,而其他例程通过通道发送更新。

    对于 SQLite,我相信最简单的方法是保持一个 sqlite 连接打开并从各种 goroutine 中使用它;尽管它确实支持同时打开多个进程,并且如果您的系统执行许多并发读取可能会更快(它使用全局锁进行写入)。

    【讨论】:

    • 谢谢。我假设 Go 中的 Web 应用程序是并发的,并且每个请求都在一个新的 go 例程中处理,因此我应该同步吗?
    • 是的,每个 http 请求都在自己的 goroutine 中运行,因此您必须在它们之间进行同步,或者使用单独的“helper” goroutine(带有通道)来处理需要序列化的事情。
    【解决方案2】:

    问题 1

    "sync" 包提供 Lock 和 RWLock,用于以常规方式同步对资源的访问。虽然这没有什么问题,但我喜欢玩成语,所以我可能会做类似this

    package main
    
    import "fmt"
    
    func SyncFile(path string) chan<- func(string) {
        ch := make(chan func(string))
        go func() {
            for process := range ch {
                process(path)
            }
        }()
        return ch
    }
    
    func main() {
        data := SyncFile("data.name")
        data <- func(path string) {
            fmt.Println(path)
        }
    }
    

    那么如果你只通过这个funcs通道访问文件,访问将是同步的。这是可靠的,但您也许可以找到更有效的方法。

    问题 2

    gosqlite3 驱动程序只是一个绑定到 libsqlite3 的 Go,所以 SQLite FAQ 适用(看起来您对此很清楚)。如果您希望这两者相互同步,那么只需将 SQLite 用法包装在文件访问同步代码中即可。

    【讨论】:

    • 您对问题 2 的回答是错误的。如果您打开一次数据库连接,您可以同时重复使用它。如果你不能,那么你的“数据库/sql”驱动程序实现有一个错误:golang.org/src/pkg/database/sql/doc.txt?s=1209:1611#L31
    • 感谢您的回答。 @voidlogic 所以,一旦我有了连接,我就可以一直使用它,并且有很多 go-routines 正在使用它?
    • @Kiril:是的,如果您的应用程序创建了一个 *sql.DB 实例,那么您可以在许多 goroutine 中使用它,而无需在代码中进行任何额外的同步。如果您在执行此操作时遇到问题,请务必将问题报告给 SQL 驱动程序实现者,因为这意味着他们的实现不是恭维/有错误。
    【解决方案3】:

    1) 您应该使用读/写互斥锁(在 go std 库中)。 代码看起来像:

    import "sync" // http://golang.org/pkg/sync/
    const (
        filename = "file.name"
    )
    var globalFileLock sync.RWMutex
    
    type DataObject struct {
      data []byte
    }
    
    func (*d DataObject) Write() {
       globalFileLock.Lock()
       defer globalFileLock.Unlock()
       ioutil.WriteFile(filename, d.data, 0644)
    }
    
    func (*d DataObject) Read() {
       globalFileLock.RLock()
       defer globalFileLock.RUnlock()
       d.data = ioutil.ReadFile(filename)
    }
    

    2) 由于您尚未发布程序中的“导入”部分,我不知道您使用的是哪个 sqlite 驱动程序。

    如果你使用database/sql打开数据库连接,驱动提供goroutines之间的并发控制。

    【讨论】:

    【解决方案4】:

    我知道这是一个老问题,但由于我遇到了同样的“数据库锁定”问题,并且一个事务为我解决了这个问题,所以我想在这里提一下:

    db, err := sql.Open("sqlite3", db_path)
    if err != nil {
        log.Printf("Cannot connect to database: %s\n", err ) 
    }
    defer db.Close()
    tx, _ := db.Begin() 
    var value string
    err = tx.QueryRow("SELECT X FROM Y").Scan(&value)
    tx.Rollback() // or: tx.Commit()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-30
      • 1970-01-01
      • 1970-01-01
      • 2012-04-18
      相关资源
      最近更新 更多