/*
   go状态协程
   在上面的例子中 我们演示了如何通过使用
   mutex来在多个协程之间共享状态
   另外一种方法是使用协程内置的同步机制来实现
   这种基于通道的方法和go的通过消息共享内存
   保证每份数据为单独的协程所有的理念一直的
 */
package main
import (
     "fmt"
     "math/rand"
     "sync/atomic"
     "time"
 )
/*
   在这个例子中 将有一个单独的协程拥有这个状态
   这样可以保证这个数据不会被并行访问所破坏
   为了读写这个状态
   其他协程将向这个协程发送信息并且相应地接收返回信息
   这些readOp writeOp 封装了这些请求和恢复
 */
type readOp struct {
     key  int
     resp chan int
 }
type writeOp struct {
     key  int
     val  int
     resp chan bool
 }
func main() {
     var ops int64 = 0
     /*
        reads 和writes通道将被其他协程用来从中读取或写入数据
     */
    reads := make(chan *readOp)
     writes := make(chan *writeOp)
    /*
       这个是拥有state的协程 state是一个协程的私有map
       这个协程不断地select通道reads writes
       当有请求来临的时候进行恢复
     */
    go func() {
         var state = make(map[int]int)
         for {
             select {
             case read := <-reads:
                 read.resp <- state[read.key]
             case write := <-writes:
                 state[write.key] = write.val
                 write.resp <- true
             }
         }
     }()
    for r := 0; r < 100; r++ {
        go func(){
            for {
                  read := &readOp{
                      key : rand.Intn(5),
                      resp: make(chan int)
                  }
                 reads <- read
                  <-read.resp
                  atomic.AddInt64(&ops, 1)
            }
            }()
     }
     for w :=0; w <10; w++ {
         go func(){
             for {
                 write := &writeOp{
                     key:randIntn(5),
                     val:rand.Intn(100).
                     resp:make(chan bool)
                 }
                writes<-write
                 <-write.resp
                 atomic.AddInt64(&ops, 1)
             }
             }()
     }
    time.Sleep(time.Second)
     opsFinal := atomic.LoadInt64(&ops)
     fmt.Println(opsFinal)
 }
/*
   基于协程的方法比基于mutex的方法更加复杂一
 */