项目场景
在 Golang1.19.x 版本下,项目中需要将日志输出到指定文件内(原生开发),故需要每隔一段时间将日志文件进行移动,并创建新的日志文件继续参与日志记录。
问题描述
在进行使用中日志文件移动时,windows 下通过 os.Rename()
不生效。
通过追踪源码,可发现该操作将导致错误 golang.org/x/sys/windows.ERROR_SHARING_VIOLATION (32)
。
// eg: D:/log/a.log -> D:/log/a.1.log
os.Rename(oldFileFullPath, backFileFullPath)
原因分析
在Golang中,"ERROR_SHARING_VIOLATION"是一个错误信息,通常与并发编程相关。它表示两个或多个Goroutines试图同时访问共享资源,而没有进行适当的同步操作,导致了竞态条件(race condition)。
要解决这个错误,可以采取以下几种方法之一:
- 使用互斥锁(Mutex):互斥锁可以确保在同一时间只有一个Goroutine能够访问共享资源。你可以在访问共享资源的代码段前加锁,并在访问完成后解锁。下面是一个示例代码片段:
package main
import (
"fmt"
"sync"
)
var sharedResource = 0
func main() {
var wg sync.WaitGroup
wg.Add(2)
// 第一个Goroutine
go func() {
defer wg.Done()
sharedResource++
}()
// 第二个Goroutine
go func() {
defer wg.Done()
sharedResource--
}()
wg.Wait()
fmt.Println("Final value of shared resource:", sharedResource)
}
在上面的示例中,使用sync.WaitGroup
来等待两个Goroutines完成,并使用互斥锁来保护对共享资源的访问。
- 使用原子操作(Atomic Operations):如果你的操作仅限于单个变量,你可以使用原子操作来确保对其的并发访问是安全的。Golang提供了
sync/atomic
包,其中包含了一些原子操作类型,例如int32
,int64
,float32
,float64
等。下面是一个使用原子操作的示例代码片段:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var sharedResource int32
var mutex sync.Mutex
func main() {
var wg sync.WaitGroup
wg.Add(2)
// 第一个Goroutine
go func() {
mutex.Lock()
atomic.AddInt32(&sharedResource, 1)
mutex.Unlock()
}()
// 第二个Goroutine
go func() {
mutex.Lock()
atomic.AddInt32(&sharedResource, -1)
mutex.Unlock()
}()
wg.Wait()
fmt.Println("Final value of shared resource:", sharedResource)
}
在上面的示例中,使用了sync.Mutex
来保护对共享资源的访问,并使用原子操作atomic.AddInt32
来安全地增加或减少sharedResource
的值。
总结
windows 环境下使用 Golang 进行使用中文件的移动操作将产生错误,该错误无法直接避免。
故,如果环境允许,可使用虚拟机或基于 docker + vscode 的开发方式,该开发方式可避免多数问题并提高工作效率。