Skip to content

Commit 7d74f6c

Browse files
committed
改为较慢但不需要hack的方式获取Goroutine ID
1 parent 5b23de2 commit 7d74f6c

File tree

5 files changed

+79
-228
lines changed

5 files changed

+79
-228
lines changed

README.md

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,74 @@
1-
[English](http://github.com/funny/sync/blob/master/README_EN.md)
2-
---------
1+
介绍
2+
====
33

4-
[中文说明](http://github.com/funny/sync/blob/master/README_CN.md)
5-
---------
4+
这个包用来在开发调试期,帮助排查程序中的死锁情况。
5+
6+
用法
7+
====
8+
9+
通常我们项目中引用到原生`sync`包的代码会像这样:
10+
11+
```go
12+
package myapp
13+
14+
import "sync"
15+
16+
var MyLock sync.Mutex
17+
18+
func MyFunc() {
19+
MyLock.Lock()
20+
defer MyLock.Unlock()
21+
22+
// .......
23+
}
24+
```
25+
26+
只需要将原来引用`sync`的代码改为引用`github.com/funny/sync`包,不需要修改别的代码:
27+
28+
29+
```go
30+
package myapp
31+
32+
import "github.com/funny/sync"
33+
34+
var MyLock sync.Mutex
35+
36+
func MyFunc() {
37+
MyLock.Lock()
38+
defer MyLock.Unlock()
39+
40+
// .......
41+
}
42+
```
43+
44+
这时候死锁诊断还没有被启用,因为做了条件编译,所以锁的开销跟原生`sync`包是一样的。
45+
46+
当需要编译一个带死锁诊断的版本的时候,在`go build --tags`列表中加入`deadlock`标签。
47+
48+
例如这样:
49+
50+
```
51+
go build -tags deadlock myproject
52+
```
53+
54+
同样这个标签也用于单元测试,否则默认的单元测试会死锁:
55+
56+
```
57+
go test -tags deadlock -v
58+
```
59+
60+
61+
原理
62+
====
63+
64+
在开启死锁检查的时候,系统会维护一份全局的锁等待列表,其次每个锁都会有当前使用者的信息。
65+
66+
当一个goroutine要等待一个锁的时候,系统会到全局的等待列表里面查找当前这个锁的使用者,是否间接或直接的正在等待当前请求锁的这个goroutine。
67+
68+
死锁不一定只发生在两个goroutine之间,极端情况也可能是一个链条状的依赖关系,又或者可能出现自身重复加锁的死锁情况。
69+
70+
当出现死锁的时候,系统将提取死锁链上的所有goroutine的堆栈跟踪信息,方便排查故障原因。
71+
72+
因为需要维护一份全局的锁等待列表,所以这里会出现额外并且集中的一个全局锁开销,会导致明显的程序的并发性能下降。
73+
74+
全局锁的问题还会再继续研究和加以改进,但是目前这个包是不能用于生产环境的,只能用在开发和调试期作为死锁诊断的辅助工具。

README_CN.md

Lines changed: 0 additions & 84 deletions
This file was deleted.

README_EN.md

Lines changed: 0 additions & 83 deletions
This file was deleted.

deadlock.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import (
66
"bytes"
77
"container/list"
88
"github.com/funny/debug"
9-
"runtime"
10-
"strconv"
119
"sync"
1210
)
1311

@@ -56,7 +54,7 @@ func (m *RWMutex) RUnlock() {
5654

5755
var (
5856
globalMutex = new(sync.Mutex)
59-
waitingList = make(map[int32]*lockUsage)
57+
waitingList = make(map[string]*lockUsage)
6058
titleStr = []byte("[DEAD LOCK]\n")
6159
goStr = []byte("goroutine ")
6260
waitStr = []byte(" wait")
@@ -69,7 +67,7 @@ var (
6967
type lockUsage struct {
7068
monitor *monitor
7169
mode byte
72-
goid int32
70+
goid string
7371
stack debug.StackInfo
7472
}
7573

@@ -81,7 +79,7 @@ func (m *monitor) wait(mode byte) *lockUsage {
8179
globalMutex.Lock()
8280
defer globalMutex.Unlock()
8381

84-
waitInfo := &lockUsage{m, mode, runtime.GetGoId(), debug.StackTrace(3, 0)}
82+
waitInfo := &lockUsage{m, mode, debug.GoroutineID(), debug.StackTrace(3)}
8583
waitingList[waitInfo.goid] = waitInfo
8684

8785
if m.holders == nil {
@@ -118,7 +116,7 @@ func (m *monitor) using(waitInfo *lockUsage) {
118116
}
119117

120118
func (m *monitor) release(mode byte) {
121-
id := runtime.GetGoId()
119+
id := debug.GoroutineID()
122120
for i := m.holders.Back(); i != nil; i = i.Prev() {
123121
if info := i.Value.(*lockUsage); info.goid == id && info.mode == mode {
124122
m.holders.Remove(i)
@@ -132,7 +130,7 @@ func deadlockPanic(waitLink []*lockUsage) {
132130
buf.Write(titleStr)
133131
for i := 0; i < len(waitLink); i++ {
134132
buf.Write(goStr)
135-
buf.WriteString(strconv.Itoa(int(waitLink[i].goid)))
133+
buf.WriteString(waitLink[i].goid)
136134
buf.Write(waitStr)
137135
if waitLink[i].mode == 'w' {
138136
buf.Write(writeStr)
@@ -153,7 +151,7 @@ func deadlockPanic(waitLink []*lockUsage) {
153151
waitHolder := j.Value.(*lockUsage)
154152
if waitHolder.goid == waitWho.goid {
155153
buf.Write(goStr)
156-
buf.WriteString(strconv.Itoa(int(waitHolder.goid)))
154+
buf.WriteString(waitHolder.goid)
157155
buf.Write(holdStr)
158156
if waitHolder.mode == 'w' {
159157
buf.Write(writeStr)

hack.sh

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)