英文:
Go "panic: sync: unlock of unlocked mutex" without a known reason
问题
我有一个使用Go语言编写的CLI应用程序(仍在开发中),源代码和依赖项都没有进行任何更改,但突然间它开始出现panic: sync: unlock of unlocked mutex
的错误。
我唯一运行并发代码的地方是处理程序被请求关闭的情况:
func handleProcTermination() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
<-c
curses.Endwin()
os.Exit(0)
}()
defer curses.Endwin()
}
我所做的唯一更改就是重命名了我的$GOPATH和工作空间文件夹。这个操作会导致这样的错误吗?
你是否遇到过类似的问题,而又没有任何解释?是否有一个合理的检查清单可以帮助找到问题的原因?
英文:
I have a cli application in Go (still in development) and no changes were made in source code neither on dependencies but all of a sudden it started to panic panic: sync: unlock of unlocked mutex
.
The only place I'm running concurrent code is to handle when program is requested to close:
func handleProcTermination() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
<-c
curses.Endwin()
os.Exit(0)
}()
defer curses.Endwin()
}
Only thing I did was to rename my $GOPATH and work space folder. Can this operation cause such error?
Have some you experienced any related problem without having any explanation? Is there a rational check list that would help to find the cause of the problem?
答案1
得分: 5
好的,以下是翻译好的内容:
好的,在一些无果的调试会话之后,作为最后的办法,我简单地从工作空间中删除了所有的第三方代码(依赖项):
cd $GOPATH
rm -rf pkg/ bin/ src/github.com src/golang.org # 这个想法是删除除了你自己的源代码之外的所有内容
使用 go get
再次获取所有使用的依赖项:
go get github.com/yadayada/yada
go get # 等等
问题解决了!应用程序正常启动,测试也通过了。不再有启动时的恐慌。看起来当你 mv
你的工作空间文件夹时会出现这个问题,但我还不确定百分之百。希望对其他人有所帮助。
从现在开始,重新安装依赖项将成为我在出现奇怪的恐慌情况时的第一步。
英文:
Ok, after some unfruitful debugging sessions, as a last resort, I simply wiped all third party code (dependencies) from the workspace:
cd $GOPATH
rm -rf pkg/ bin/ src/github.com src/golang.org # the idea is to remove all except your own source
Used go get
to get all used dependencies again:
go get github.com/yadayada/yada
go get # etc
And the problem is gone! Application is starting normally and tests are passing. No startup panics anymore. It looks like this problem happens when you mv
your work space folder but I'm not 100% sure yet. Hope it helps someone else.
From now on, re install dependencies will be my first step when weird panic conditions like that suddenly appear.
答案2
得分: 3
你没有提供太多信息,所以我的回答比较笼统。
理论上,并发代码中的错误可能长时间未被注意到,然后突然出现。实际上,如果错误很容易重现(几乎每次运行都发生),这通常意味着代码或环境发生了变化。
解决方案是进行调试。
了解发生了什么变化有助于确定错误。在这种情况下,似乎锁定/解锁对没有匹配。如果你没有在线程之间传递锁定,你应该能够找到在线程内部没有获取锁定或提前释放锁定的代码路径。在某些关键点上放置断言来验证你认为自己持有锁定可能会有所帮助。
英文:
You're not giving much information to go on, so my answer is generic.
In theory, bugs in concurrent code can remain unnoticed for a long time and then suddenly show up. In practice, if the bug is easily repeatable (happens nearly every run) this usually indicates that something did change in the code or environment.
The solution: debug.
Knowing what has changed can be helpful to identify the bug. In this case, it appears that lock/unlock pairs or not matching up. If you are not passing locks between threads, you should be able to find a code path within the thread that has not acquired the lock, or has released it early. It may be helpful to put assertions at certain points to validate that you are holding the lock when you think you are.
答案3
得分: 1
确保你不要将锁复制到其他地方。
在并发环境中,看似无懈可击的代码可能会出现这样的情况:包含代码的结构体被复制到其他地方,导致底层的锁不同。
考虑以下代码片段:
type someStruct struct {
lock sync.Mutex
}
func (s *someStruct) DoSomethingUnderLock() {
s.lock.Lock()
defer s.lock.Unlock() // 这会导致 panic
time.Sleep(200 * time.Millisecond)
}
func main() {
s1 := &someStruct{}
go func() {
time.Sleep(100 * time.Millisecond) // 等待 DoSomethingUnderLock 获取锁
s2 := &someStruct{}
*s1 = *s2
}()
s1.DoSomethingUnderLock()
}
这里的关键是 *s1 = *s2
,它导致同一个接收器函数使用了不同的锁,如果在锁被获取时替换了结构体,我们将得到 sync: unlock of unlocked mutex
。
导致调试变得更加困难的是,someStruct
可能嵌套在另一个结构体中(还可能是另一个结构体的嵌套),如果外部结构体以类似的方式被替换(只要 someStruct
在那里不是一个引用),结果将是相同的。
如果是这种情况,你可以使用锁的引用(或整个结构体的引用)。现在你需要初始化它,但这是一个小代价,可能会避免一些难以理解的错误。请参考这里不会发生 panic 的修改后的代码。
英文:
Make sure you don't copy the lock somewhere.
What can happen with seemingly bulletproof code in concurrent environments is that the struct including the code gets copied elsewhere, which results in the underlying lock being different.
Consider this code snippet:
type someStruct struct {
lock sync.Mutex
}
func (s *someStruct) DoSomethingUnderLock() {
s.lock.Lock()
defer s.lock.Unlock() // This will panic
time.Sleep(200 * time.Millisecond)
}
func main() {
s1 := &someStruct{}
go func() {
time.Sleep(100 * time.Millisecond) // Wait until DoSomethingUnderLock takes the lock
s2 := &someStruct{}
*s1 = *s2
}()
s1.DoSomethingUnderLock()
}
*s1 = *s2
is the key here - it results in a different lock being used by the same receiver function and if the struct is replaced while the lock is taken, we'll get sync: unlock of unlocked mutex
.
What makes it harder to debug is that someStruct
might be nested in another struct (and another, and another), and if the outer struct gets replaced (as long as someStruct
is not a reference there) in a similar manner, the result will be the same.
If this is the case, you can use a reference to the lock (or the whole struct) instead. Now you need to initialize it, but it's a small price that might save you some obscure bugs. See the modified code that doesn't panic here.
答案4
得分: 0
对于那些来到这里但没有解决问题的人,请检查应用程序是否在一个版本的Linux中编译,但在另一个版本中运行。至少在我的情况下是这样发生的。
英文:
For those who come here and didn't solve your problem. Check If the application is compiled in one version of Linux but running in another version. At least in my case it happened.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论