A Go map thread safety problem while reading a cache DIY book












0















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?










share|improve this question





























    0















    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?










    share|improve this question



























      0












      0








      0


      1






      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?










      share|improve this question
















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 1 at 11:23









      Flimzy

      38.7k106597




      38.7k106597










      asked Jan 1 at 3:27









      S.HzjS.Hzj

      143




      143
























          1 Answer
          1






          active

          oldest

          votes


















          0














          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






          share|improve this answer


























          • 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 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











          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
          });


          }
          });














          draft saved

          draft discarded


















          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









          0














          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






          share|improve this answer


























          • 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 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
















          0














          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






          share|improve this answer


























          • 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 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














          0












          0








          0







          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






          share|improve this answer















          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







          share|improve this answer














          share|improve this answer



          share|improve this answer








          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 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











          • @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











          • @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











          • @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




















          draft saved

          draft discarded




















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Mossoró

          Error while reading .h5 file using the rhdf5 package in R

          Pushsharp Apns notification error: 'InvalidToken'