A Go map thread safety problem while reading a cache DIY book
I am reading a book which teaches me how to write a simple cache like redis.
With a goal to implement a distribute hash, the project must have key migrate, which needs an iterator. And I think there may be some problems.
His book about iterating a map, but while the iteration, the hold of read lock not continuously. The reason is trying not to effect main cache process. I believe there must be a thread safety problem because the main cache thread is still writing to map. I wrote a demo, but not sure.
//book code
type inMemoryScanner struct {
pair
pair Chan *pair
closeCh chan struct{}
}
func (c *inMemoryCache) NewScanner() Scanner {
pairCh := make(chan *pair)
closeCh := make(chan struct{})
go func() {
defer close(pairCh)
c.mutex.RLock()
//the c.c is book's map
for k, v := range c.c {
c.mutex.RUnlock()
select {
case <-closeCh:
return
case pairCh <- &pair{k, v}:
}
c.mutex.RLock()
}
c.mutex.RUnLock()
}
return &inMemoryScanner{pair{}, pairCh, closeCh}
}
//my demo
func main() {
testMap := make(map[string]string)
mutex := sync.RWMutex{}
for i := 0; i < 64; i ++ {
mutex.Lock()
testMap[uuid.New().String()] = uuid.New().String()
mutex.Unlock()
fmt.Println("Write")
}
go func() {
for {
mutex.Lock()
testMap[uuid.New().String()] = uuid.New().String()
time.Sleep(100 * time.Millisecond)
mutex.Unlock()
fmt.Println("Write")
}
} ()
for k, v := range testMap {
mutex.RLock()
fmt.Println("k" + k + "v" + v)
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
}
In my demo, the 'Write' and the map's result amount not equal! And I believe, In an reality project, the rebalance can't be once, there must be continuous background work, doesn't it?
multithreading go caching concurrency thread-safety
add a comment |
I am reading a book which teaches me how to write a simple cache like redis.
With a goal to implement a distribute hash, the project must have key migrate, which needs an iterator. And I think there may be some problems.
His book about iterating a map, but while the iteration, the hold of read lock not continuously. The reason is trying not to effect main cache process. I believe there must be a thread safety problem because the main cache thread is still writing to map. I wrote a demo, but not sure.
//book code
type inMemoryScanner struct {
pair
pair Chan *pair
closeCh chan struct{}
}
func (c *inMemoryCache) NewScanner() Scanner {
pairCh := make(chan *pair)
closeCh := make(chan struct{})
go func() {
defer close(pairCh)
c.mutex.RLock()
//the c.c is book's map
for k, v := range c.c {
c.mutex.RUnlock()
select {
case <-closeCh:
return
case pairCh <- &pair{k, v}:
}
c.mutex.RLock()
}
c.mutex.RUnLock()
}
return &inMemoryScanner{pair{}, pairCh, closeCh}
}
//my demo
func main() {
testMap := make(map[string]string)
mutex := sync.RWMutex{}
for i := 0; i < 64; i ++ {
mutex.Lock()
testMap[uuid.New().String()] = uuid.New().String()
mutex.Unlock()
fmt.Println("Write")
}
go func() {
for {
mutex.Lock()
testMap[uuid.New().String()] = uuid.New().String()
time.Sleep(100 * time.Millisecond)
mutex.Unlock()
fmt.Println("Write")
}
} ()
for k, v := range testMap {
mutex.RLock()
fmt.Println("k" + k + "v" + v)
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
}
In my demo, the 'Write' and the map's result amount not equal! And I believe, In an reality project, the rebalance can't be once, there must be continuous background work, doesn't it?
multithreading go caching concurrency thread-safety
add a comment |
I am reading a book which teaches me how to write a simple cache like redis.
With a goal to implement a distribute hash, the project must have key migrate, which needs an iterator. And I think there may be some problems.
His book about iterating a map, but while the iteration, the hold of read lock not continuously. The reason is trying not to effect main cache process. I believe there must be a thread safety problem because the main cache thread is still writing to map. I wrote a demo, but not sure.
//book code
type inMemoryScanner struct {
pair
pair Chan *pair
closeCh chan struct{}
}
func (c *inMemoryCache) NewScanner() Scanner {
pairCh := make(chan *pair)
closeCh := make(chan struct{})
go func() {
defer close(pairCh)
c.mutex.RLock()
//the c.c is book's map
for k, v := range c.c {
c.mutex.RUnlock()
select {
case <-closeCh:
return
case pairCh <- &pair{k, v}:
}
c.mutex.RLock()
}
c.mutex.RUnLock()
}
return &inMemoryScanner{pair{}, pairCh, closeCh}
}
//my demo
func main() {
testMap := make(map[string]string)
mutex := sync.RWMutex{}
for i := 0; i < 64; i ++ {
mutex.Lock()
testMap[uuid.New().String()] = uuid.New().String()
mutex.Unlock()
fmt.Println("Write")
}
go func() {
for {
mutex.Lock()
testMap[uuid.New().String()] = uuid.New().String()
time.Sleep(100 * time.Millisecond)
mutex.Unlock()
fmt.Println("Write")
}
} ()
for k, v := range testMap {
mutex.RLock()
fmt.Println("k" + k + "v" + v)
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
}
In my demo, the 'Write' and the map's result amount not equal! And I believe, In an reality project, the rebalance can't be once, there must be continuous background work, doesn't it?
multithreading go caching concurrency thread-safety
I am reading a book which teaches me how to write a simple cache like redis.
With a goal to implement a distribute hash, the project must have key migrate, which needs an iterator. And I think there may be some problems.
His book about iterating a map, but while the iteration, the hold of read lock not continuously. The reason is trying not to effect main cache process. I believe there must be a thread safety problem because the main cache thread is still writing to map. I wrote a demo, but not sure.
//book code
type inMemoryScanner struct {
pair
pair Chan *pair
closeCh chan struct{}
}
func (c *inMemoryCache) NewScanner() Scanner {
pairCh := make(chan *pair)
closeCh := make(chan struct{})
go func() {
defer close(pairCh)
c.mutex.RLock()
//the c.c is book's map
for k, v := range c.c {
c.mutex.RUnlock()
select {
case <-closeCh:
return
case pairCh <- &pair{k, v}:
}
c.mutex.RLock()
}
c.mutex.RUnLock()
}
return &inMemoryScanner{pair{}, pairCh, closeCh}
}
//my demo
func main() {
testMap := make(map[string]string)
mutex := sync.RWMutex{}
for i := 0; i < 64; i ++ {
mutex.Lock()
testMap[uuid.New().String()] = uuid.New().String()
mutex.Unlock()
fmt.Println("Write")
}
go func() {
for {
mutex.Lock()
testMap[uuid.New().String()] = uuid.New().String()
time.Sleep(100 * time.Millisecond)
mutex.Unlock()
fmt.Println("Write")
}
} ()
for k, v := range testMap {
mutex.RLock()
fmt.Println("k" + k + "v" + v)
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
}
In my demo, the 'Write' and the map's result amount not equal! And I believe, In an reality project, the rebalance can't be once, there must be continuous background work, doesn't it?
multithreading go caching concurrency thread-safety
multithreading go caching concurrency thread-safety
edited Jan 1 at 11:23
Flimzy
38.7k106597
38.7k106597
asked Jan 1 at 3:27
S.HzjS.Hzj
143
143
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
You have a data race. Your results are undefined.
Simplifying your code so that it compiles and runs,
package main
import (
"sync"
"time"
)
func main() {
testMap := make(map[string]string)
mutex := sync.RWMutex{}
for i := 0; i < 64; i++ {
mutex.Lock()
now := time.Now().String()
testMap[now] = now
mutex.Unlock()
}
go func() {
for {
mutex.Lock()
now := time.Now().String()
testMap[now] = now
time.Sleep(100 * time.Millisecond)
mutex.Unlock()
}
}()
for k, v := range testMap {
mutex.RLock()
_, _ = k, v
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
}
Output:
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x00c00008c060 by main goroutine:
runtime.mapiternext()
/home/peter/go/src/runtime/map.go:844 +0x0
main.main()
/home/peter/gopath/src/racer.go:26 +0x217
Previous write at 0x00c00008c060 by goroutine 5:
runtime.mapassign_faststr()
/home/peter/go/src/runtime/map_faststr.go:202 +0x0
main.main.func1()
/home/peter/gopath/src/racer.go:21 +0x9b
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:17 +0x17b
==================
==================
WARNING: DATA RACE
Read at 0x00c0000a6638 by main goroutine:
main.main()
/home/peter/gopath/src/racer.go:26 +0x1d0
Previous write at 0x00c0000a6638 by goroutine 5:
main.main.func1()
/home/peter/gopath/src/racer.go:21 +0xb0
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:17 +0x17b
==================
fatal error: concurrent map iteration and map write
goroutine 1 [running]:
runtime.throw(0x4b1eb7, 0x26)
/home/peter/go/src/runtime/panic.go:617 +0x72 fp=0xc000059e48 sp=0xc000059e18 pc=0x44d722
runtime.mapiternext(0xc000059f28)
/home/peter/go/src/runtime/map.go:851 +0x55e fp=0xc000059ed0 sp=0xc000059e48 pc=0x434c2e
main.main()
/home/peter/gopath/src/racer.go:26 +0x218 fp=0xc000059f98 sp=0xc000059ed0 pc=0x48a1f8
runtime.main()
/home/peter/go/src/runtime/proc.go:200 +0x20c fp=0xc000059fe0 sp=0xc000059f98 pc=0x44f06c
runtime.goexit()
/home/peter/go/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc000059fe8 sp=0xc000059fe0 pc=0x475751
goroutine 4 [sleep]:
runtime.goparkunlock(...)
/home/peter/go/src/runtime/proc.go:307
time.Sleep(0x5f5e100)
/home/peter/go/src/runtime/time.go:105 +0x159
main.main.func1(0xc00001c280, 0xc00008c060)
/home/peter/gopath/src/racer.go:22 +0x3e
created by main.main
/home/peter/gopath/src/racer.go:17 +0x17c
exit status 2
$
You are not locking the map reads,
for k, v := range testMap {
mutex.RLock()
_, _ = k, v
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
for k, v := range testMap { ... } reads the map. k, v are local variables.
You need to lock the map reads,
mutex.RLock()
for k, v := range testMap {
_, _ = k, v
time.Sleep(100 * time.Millisecond)
}
mutex.RUnlock()
Go: Data Race Detector
The Go Blog: Introducing the Go Race Detector
GopherCon 2016: Keith Randall - Inside the Map Implementation
Understand.So the strategy in book is definitely wrong racing? Am I right? If hold lock in this way, one iteration is not enough, must be background continuously task?
– S.Hzj
Jan 1 at 16:46
@S.Hzj: The fundamental startegy for locking is to minimize the time the lock is held. Reading an entire map usually takes some (a long) time. A Gomapis a dynamic structure that is continuosly changed as the map is modified.
– peterSO
Jan 1 at 18:13
@S.Hzj The go thing is to express your problem properly in your program; locks almost never do this. If you need an iterator over your store which reflects the point-in-time state, then make just that, and use channels (thus messages) to express it. If you can have a looser iterator, again express it with the normal constructs; only rely on locks when you are truly cornered.
– mevets
Jan 2 at 7:32
@mevets: "only rely on locks when you are truly cornered." Experts disagree with you: The Go Programming Language.
– peterSO
Jan 2 at 13:37
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53992845%2fa-go-map-thread-safety-problem-while-reading-a-cache-diy-book%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
You have a data race. Your results are undefined.
Simplifying your code so that it compiles and runs,
package main
import (
"sync"
"time"
)
func main() {
testMap := make(map[string]string)
mutex := sync.RWMutex{}
for i := 0; i < 64; i++ {
mutex.Lock()
now := time.Now().String()
testMap[now] = now
mutex.Unlock()
}
go func() {
for {
mutex.Lock()
now := time.Now().String()
testMap[now] = now
time.Sleep(100 * time.Millisecond)
mutex.Unlock()
}
}()
for k, v := range testMap {
mutex.RLock()
_, _ = k, v
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
}
Output:
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x00c00008c060 by main goroutine:
runtime.mapiternext()
/home/peter/go/src/runtime/map.go:844 +0x0
main.main()
/home/peter/gopath/src/racer.go:26 +0x217
Previous write at 0x00c00008c060 by goroutine 5:
runtime.mapassign_faststr()
/home/peter/go/src/runtime/map_faststr.go:202 +0x0
main.main.func1()
/home/peter/gopath/src/racer.go:21 +0x9b
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:17 +0x17b
==================
==================
WARNING: DATA RACE
Read at 0x00c0000a6638 by main goroutine:
main.main()
/home/peter/gopath/src/racer.go:26 +0x1d0
Previous write at 0x00c0000a6638 by goroutine 5:
main.main.func1()
/home/peter/gopath/src/racer.go:21 +0xb0
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:17 +0x17b
==================
fatal error: concurrent map iteration and map write
goroutine 1 [running]:
runtime.throw(0x4b1eb7, 0x26)
/home/peter/go/src/runtime/panic.go:617 +0x72 fp=0xc000059e48 sp=0xc000059e18 pc=0x44d722
runtime.mapiternext(0xc000059f28)
/home/peter/go/src/runtime/map.go:851 +0x55e fp=0xc000059ed0 sp=0xc000059e48 pc=0x434c2e
main.main()
/home/peter/gopath/src/racer.go:26 +0x218 fp=0xc000059f98 sp=0xc000059ed0 pc=0x48a1f8
runtime.main()
/home/peter/go/src/runtime/proc.go:200 +0x20c fp=0xc000059fe0 sp=0xc000059f98 pc=0x44f06c
runtime.goexit()
/home/peter/go/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc000059fe8 sp=0xc000059fe0 pc=0x475751
goroutine 4 [sleep]:
runtime.goparkunlock(...)
/home/peter/go/src/runtime/proc.go:307
time.Sleep(0x5f5e100)
/home/peter/go/src/runtime/time.go:105 +0x159
main.main.func1(0xc00001c280, 0xc00008c060)
/home/peter/gopath/src/racer.go:22 +0x3e
created by main.main
/home/peter/gopath/src/racer.go:17 +0x17c
exit status 2
$
You are not locking the map reads,
for k, v := range testMap {
mutex.RLock()
_, _ = k, v
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
for k, v := range testMap { ... } reads the map. k, v are local variables.
You need to lock the map reads,
mutex.RLock()
for k, v := range testMap {
_, _ = k, v
time.Sleep(100 * time.Millisecond)
}
mutex.RUnlock()
Go: Data Race Detector
The Go Blog: Introducing the Go Race Detector
GopherCon 2016: Keith Randall - Inside the Map Implementation
Understand.So the strategy in book is definitely wrong racing? Am I right? If hold lock in this way, one iteration is not enough, must be background continuously task?
– S.Hzj
Jan 1 at 16:46
@S.Hzj: The fundamental startegy for locking is to minimize the time the lock is held. Reading an entire map usually takes some (a long) time. A Gomapis a dynamic structure that is continuosly changed as the map is modified.
– peterSO
Jan 1 at 18:13
@S.Hzj The go thing is to express your problem properly in your program; locks almost never do this. If you need an iterator over your store which reflects the point-in-time state, then make just that, and use channels (thus messages) to express it. If you can have a looser iterator, again express it with the normal constructs; only rely on locks when you are truly cornered.
– mevets
Jan 2 at 7:32
@mevets: "only rely on locks when you are truly cornered." Experts disagree with you: The Go Programming Language.
– peterSO
Jan 2 at 13:37
add a comment |
You have a data race. Your results are undefined.
Simplifying your code so that it compiles and runs,
package main
import (
"sync"
"time"
)
func main() {
testMap := make(map[string]string)
mutex := sync.RWMutex{}
for i := 0; i < 64; i++ {
mutex.Lock()
now := time.Now().String()
testMap[now] = now
mutex.Unlock()
}
go func() {
for {
mutex.Lock()
now := time.Now().String()
testMap[now] = now
time.Sleep(100 * time.Millisecond)
mutex.Unlock()
}
}()
for k, v := range testMap {
mutex.RLock()
_, _ = k, v
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
}
Output:
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x00c00008c060 by main goroutine:
runtime.mapiternext()
/home/peter/go/src/runtime/map.go:844 +0x0
main.main()
/home/peter/gopath/src/racer.go:26 +0x217
Previous write at 0x00c00008c060 by goroutine 5:
runtime.mapassign_faststr()
/home/peter/go/src/runtime/map_faststr.go:202 +0x0
main.main.func1()
/home/peter/gopath/src/racer.go:21 +0x9b
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:17 +0x17b
==================
==================
WARNING: DATA RACE
Read at 0x00c0000a6638 by main goroutine:
main.main()
/home/peter/gopath/src/racer.go:26 +0x1d0
Previous write at 0x00c0000a6638 by goroutine 5:
main.main.func1()
/home/peter/gopath/src/racer.go:21 +0xb0
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:17 +0x17b
==================
fatal error: concurrent map iteration and map write
goroutine 1 [running]:
runtime.throw(0x4b1eb7, 0x26)
/home/peter/go/src/runtime/panic.go:617 +0x72 fp=0xc000059e48 sp=0xc000059e18 pc=0x44d722
runtime.mapiternext(0xc000059f28)
/home/peter/go/src/runtime/map.go:851 +0x55e fp=0xc000059ed0 sp=0xc000059e48 pc=0x434c2e
main.main()
/home/peter/gopath/src/racer.go:26 +0x218 fp=0xc000059f98 sp=0xc000059ed0 pc=0x48a1f8
runtime.main()
/home/peter/go/src/runtime/proc.go:200 +0x20c fp=0xc000059fe0 sp=0xc000059f98 pc=0x44f06c
runtime.goexit()
/home/peter/go/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc000059fe8 sp=0xc000059fe0 pc=0x475751
goroutine 4 [sleep]:
runtime.goparkunlock(...)
/home/peter/go/src/runtime/proc.go:307
time.Sleep(0x5f5e100)
/home/peter/go/src/runtime/time.go:105 +0x159
main.main.func1(0xc00001c280, 0xc00008c060)
/home/peter/gopath/src/racer.go:22 +0x3e
created by main.main
/home/peter/gopath/src/racer.go:17 +0x17c
exit status 2
$
You are not locking the map reads,
for k, v := range testMap {
mutex.RLock()
_, _ = k, v
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
for k, v := range testMap { ... } reads the map. k, v are local variables.
You need to lock the map reads,
mutex.RLock()
for k, v := range testMap {
_, _ = k, v
time.Sleep(100 * time.Millisecond)
}
mutex.RUnlock()
Go: Data Race Detector
The Go Blog: Introducing the Go Race Detector
GopherCon 2016: Keith Randall - Inside the Map Implementation
Understand.So the strategy in book is definitely wrong racing? Am I right? If hold lock in this way, one iteration is not enough, must be background continuously task?
– S.Hzj
Jan 1 at 16:46
@S.Hzj: The fundamental startegy for locking is to minimize the time the lock is held. Reading an entire map usually takes some (a long) time. A Gomapis a dynamic structure that is continuosly changed as the map is modified.
– peterSO
Jan 1 at 18:13
@S.Hzj The go thing is to express your problem properly in your program; locks almost never do this. If you need an iterator over your store which reflects the point-in-time state, then make just that, and use channels (thus messages) to express it. If you can have a looser iterator, again express it with the normal constructs; only rely on locks when you are truly cornered.
– mevets
Jan 2 at 7:32
@mevets: "only rely on locks when you are truly cornered." Experts disagree with you: The Go Programming Language.
– peterSO
Jan 2 at 13:37
add a comment |
You have a data race. Your results are undefined.
Simplifying your code so that it compiles and runs,
package main
import (
"sync"
"time"
)
func main() {
testMap := make(map[string]string)
mutex := sync.RWMutex{}
for i := 0; i < 64; i++ {
mutex.Lock()
now := time.Now().String()
testMap[now] = now
mutex.Unlock()
}
go func() {
for {
mutex.Lock()
now := time.Now().String()
testMap[now] = now
time.Sleep(100 * time.Millisecond)
mutex.Unlock()
}
}()
for k, v := range testMap {
mutex.RLock()
_, _ = k, v
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
}
Output:
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x00c00008c060 by main goroutine:
runtime.mapiternext()
/home/peter/go/src/runtime/map.go:844 +0x0
main.main()
/home/peter/gopath/src/racer.go:26 +0x217
Previous write at 0x00c00008c060 by goroutine 5:
runtime.mapassign_faststr()
/home/peter/go/src/runtime/map_faststr.go:202 +0x0
main.main.func1()
/home/peter/gopath/src/racer.go:21 +0x9b
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:17 +0x17b
==================
==================
WARNING: DATA RACE
Read at 0x00c0000a6638 by main goroutine:
main.main()
/home/peter/gopath/src/racer.go:26 +0x1d0
Previous write at 0x00c0000a6638 by goroutine 5:
main.main.func1()
/home/peter/gopath/src/racer.go:21 +0xb0
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:17 +0x17b
==================
fatal error: concurrent map iteration and map write
goroutine 1 [running]:
runtime.throw(0x4b1eb7, 0x26)
/home/peter/go/src/runtime/panic.go:617 +0x72 fp=0xc000059e48 sp=0xc000059e18 pc=0x44d722
runtime.mapiternext(0xc000059f28)
/home/peter/go/src/runtime/map.go:851 +0x55e fp=0xc000059ed0 sp=0xc000059e48 pc=0x434c2e
main.main()
/home/peter/gopath/src/racer.go:26 +0x218 fp=0xc000059f98 sp=0xc000059ed0 pc=0x48a1f8
runtime.main()
/home/peter/go/src/runtime/proc.go:200 +0x20c fp=0xc000059fe0 sp=0xc000059f98 pc=0x44f06c
runtime.goexit()
/home/peter/go/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc000059fe8 sp=0xc000059fe0 pc=0x475751
goroutine 4 [sleep]:
runtime.goparkunlock(...)
/home/peter/go/src/runtime/proc.go:307
time.Sleep(0x5f5e100)
/home/peter/go/src/runtime/time.go:105 +0x159
main.main.func1(0xc00001c280, 0xc00008c060)
/home/peter/gopath/src/racer.go:22 +0x3e
created by main.main
/home/peter/gopath/src/racer.go:17 +0x17c
exit status 2
$
You are not locking the map reads,
for k, v := range testMap {
mutex.RLock()
_, _ = k, v
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
for k, v := range testMap { ... } reads the map. k, v are local variables.
You need to lock the map reads,
mutex.RLock()
for k, v := range testMap {
_, _ = k, v
time.Sleep(100 * time.Millisecond)
}
mutex.RUnlock()
Go: Data Race Detector
The Go Blog: Introducing the Go Race Detector
GopherCon 2016: Keith Randall - Inside the Map Implementation
You have a data race. Your results are undefined.
Simplifying your code so that it compiles and runs,
package main
import (
"sync"
"time"
)
func main() {
testMap := make(map[string]string)
mutex := sync.RWMutex{}
for i := 0; i < 64; i++ {
mutex.Lock()
now := time.Now().String()
testMap[now] = now
mutex.Unlock()
}
go func() {
for {
mutex.Lock()
now := time.Now().String()
testMap[now] = now
time.Sleep(100 * time.Millisecond)
mutex.Unlock()
}
}()
for k, v := range testMap {
mutex.RLock()
_, _ = k, v
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
}
Output:
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x00c00008c060 by main goroutine:
runtime.mapiternext()
/home/peter/go/src/runtime/map.go:844 +0x0
main.main()
/home/peter/gopath/src/racer.go:26 +0x217
Previous write at 0x00c00008c060 by goroutine 5:
runtime.mapassign_faststr()
/home/peter/go/src/runtime/map_faststr.go:202 +0x0
main.main.func1()
/home/peter/gopath/src/racer.go:21 +0x9b
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:17 +0x17b
==================
==================
WARNING: DATA RACE
Read at 0x00c0000a6638 by main goroutine:
main.main()
/home/peter/gopath/src/racer.go:26 +0x1d0
Previous write at 0x00c0000a6638 by goroutine 5:
main.main.func1()
/home/peter/gopath/src/racer.go:21 +0xb0
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:17 +0x17b
==================
fatal error: concurrent map iteration and map write
goroutine 1 [running]:
runtime.throw(0x4b1eb7, 0x26)
/home/peter/go/src/runtime/panic.go:617 +0x72 fp=0xc000059e48 sp=0xc000059e18 pc=0x44d722
runtime.mapiternext(0xc000059f28)
/home/peter/go/src/runtime/map.go:851 +0x55e fp=0xc000059ed0 sp=0xc000059e48 pc=0x434c2e
main.main()
/home/peter/gopath/src/racer.go:26 +0x218 fp=0xc000059f98 sp=0xc000059ed0 pc=0x48a1f8
runtime.main()
/home/peter/go/src/runtime/proc.go:200 +0x20c fp=0xc000059fe0 sp=0xc000059f98 pc=0x44f06c
runtime.goexit()
/home/peter/go/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc000059fe8 sp=0xc000059fe0 pc=0x475751
goroutine 4 [sleep]:
runtime.goparkunlock(...)
/home/peter/go/src/runtime/proc.go:307
time.Sleep(0x5f5e100)
/home/peter/go/src/runtime/time.go:105 +0x159
main.main.func1(0xc00001c280, 0xc00008c060)
/home/peter/gopath/src/racer.go:22 +0x3e
created by main.main
/home/peter/gopath/src/racer.go:17 +0x17c
exit status 2
$
You are not locking the map reads,
for k, v := range testMap {
mutex.RLock()
_, _ = k, v
mutex.RUnlock()
time.Sleep(100 * time.Millisecond)
}
for k, v := range testMap { ... } reads the map. k, v are local variables.
You need to lock the map reads,
mutex.RLock()
for k, v := range testMap {
_, _ = k, v
time.Sleep(100 * time.Millisecond)
}
mutex.RUnlock()
Go: Data Race Detector
The Go Blog: Introducing the Go Race Detector
GopherCon 2016: Keith Randall - Inside the Map Implementation
edited Jan 1 at 18:15
answered Jan 1 at 4:31
peterSOpeterSO
95.3k14160176
95.3k14160176
Understand.So the strategy in book is definitely wrong racing? Am I right? If hold lock in this way, one iteration is not enough, must be background continuously task?
– S.Hzj
Jan 1 at 16:46
@S.Hzj: The fundamental startegy for locking is to minimize the time the lock is held. Reading an entire map usually takes some (a long) time. A Gomapis a dynamic structure that is continuosly changed as the map is modified.
– peterSO
Jan 1 at 18:13
@S.Hzj The go thing is to express your problem properly in your program; locks almost never do this. If you need an iterator over your store which reflects the point-in-time state, then make just that, and use channels (thus messages) to express it. If you can have a looser iterator, again express it with the normal constructs; only rely on locks when you are truly cornered.
– mevets
Jan 2 at 7:32
@mevets: "only rely on locks when you are truly cornered." Experts disagree with you: The Go Programming Language.
– peterSO
Jan 2 at 13:37
add a comment |
Understand.So the strategy in book is definitely wrong racing? Am I right? If hold lock in this way, one iteration is not enough, must be background continuously task?
– S.Hzj
Jan 1 at 16:46
@S.Hzj: The fundamental startegy for locking is to minimize the time the lock is held. Reading an entire map usually takes some (a long) time. A Gomapis a dynamic structure that is continuosly changed as the map is modified.
– peterSO
Jan 1 at 18:13
@S.Hzj The go thing is to express your problem properly in your program; locks almost never do this. If you need an iterator over your store which reflects the point-in-time state, then make just that, and use channels (thus messages) to express it. If you can have a looser iterator, again express it with the normal constructs; only rely on locks when you are truly cornered.
– mevets
Jan 2 at 7:32
@mevets: "only rely on locks when you are truly cornered." Experts disagree with you: The Go Programming Language.
– peterSO
Jan 2 at 13:37
Understand.So the strategy in book is definitely wrong racing? Am I right? If hold lock in this way, one iteration is not enough, must be background continuously task?
– S.Hzj
Jan 1 at 16:46
Understand.So the strategy in book is definitely wrong racing? Am I right? If hold lock in this way, one iteration is not enough, must be background continuously task?
– S.Hzj
Jan 1 at 16:46
@S.Hzj: The fundamental startegy for locking is to minimize the time the lock is held. Reading an entire map usually takes some (a long) time. A Go
map is a dynamic structure that is continuosly changed as the map is modified.– peterSO
Jan 1 at 18:13
@S.Hzj: The fundamental startegy for locking is to minimize the time the lock is held. Reading an entire map usually takes some (a long) time. A Go
map is a dynamic structure that is continuosly changed as the map is modified.– peterSO
Jan 1 at 18:13
@S.Hzj The go thing is to express your problem properly in your program; locks almost never do this. If you need an iterator over your store which reflects the point-in-time state, then make just that, and use channels (thus messages) to express it. If you can have a looser iterator, again express it with the normal constructs; only rely on locks when you are truly cornered.
– mevets
Jan 2 at 7:32
@S.Hzj The go thing is to express your problem properly in your program; locks almost never do this. If you need an iterator over your store which reflects the point-in-time state, then make just that, and use channels (thus messages) to express it. If you can have a looser iterator, again express it with the normal constructs; only rely on locks when you are truly cornered.
– mevets
Jan 2 at 7:32
@mevets: "only rely on locks when you are truly cornered." Experts disagree with you: The Go Programming Language.
– peterSO
Jan 2 at 13:37
@mevets: "only rely on locks when you are truly cornered." Experts disagree with you: The Go Programming Language.
– peterSO
Jan 2 at 13:37
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53992845%2fa-go-map-thread-safety-problem-while-reading-a-cache-diy-book%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown