Golang 协程 && 管道
1 package threadts 2 3 import ( 4 "fmt" 5 "runtime" 6 "sync" 7 "time" 8 ) 9 10 /* 11 Golang中的协程和主线程:gorou 12 1.一个Go线程上,可以起多个协程.也可以理解成:协程是轻量级的线程[编译器做优化] 13 2.Go协程的特点 14 有独立的栈空间 15 共享程序堆空间 16 调度由用户控制 17 协程是轻量级的线程 18 3.主线程退出了,协程不管有没有执行完都会终止 19 4.主线程是一个物理线程,直接作用在cpu上的.是重量级的,非常消耗cpu资源 20 5.协程从主线程开启的,是轻量级的线程,是逻辑态.对资源消耗非常小 21 6.Glang的协程机制是重要的特点,可以轻松的开启上万个协程. 22 7.注意:如果在协程代码中出现panic,没有及时用defer+recover来处理,就会导致整个程序崩溃 23 24 MPG 25 Mc:操作系统的主线程 26 P:协程执行需要的上下文 27 G:协程 28 29 编译的时候加上"-race"参数,即可显示程序运行存在的资源竞争问题. 30 go build -race main.go 31 */ 32 33 // 全局变量 34 var ( 35 myMap = make(map[int]int, 1) 36 intChan = make(chan int, 1) 37 38 // 互斥锁 39 lock sync.Mutex 40 ) 41 42 /* 43 A处Sleep,B处不Sleep能运行 结论:管道为空的时候会阻塞 44 45 B处Sleep,A处不Sleep能运行 结论:管道满了,还要在写的话就得等有人把数据取走 46 当所有协程都处于休眠状态,或者所有协程都退出,而代码仍然要从空管道读取,或者往满了的管道写入都会报错(fatal error: all goroutines are asleep - deadlock!) 47 */ 48 func Test1() { 49 func1 := func() { 50 for i := 1; i <= 100; i++ { 51 fmt.Printf("intChan <- %v \n", i) 52 intChan <- i 53 // time.Sleep(time.Second * 1) // A 54 } 55 close(intChan) 56 } 57 go func1() 58 for v := range intChan { 59 fmt.Printf("%v <- intChan \n", v) 60 time.Sleep(time.Second * 1) // B 61 } 62 } 63 64 func Test2() { 65 var chan1 chan int = make(chan int, 1) 66 var chan2 chan string = make(chan string, 1) 67 func1 := func() { 68 for { 69 select { // 注意:在这里break continue不好使。你可以用return 或这带label去break 70 case t1 := <-chan1: 71 fmt.Println("chan1读到数据了:", t1) 72 case t2 := <-chan2: 73 fmt.Println("chan2读到数据了:", t2) 74 default: 75 fmt.Println("func1读数据被堵塞了!") 76 77 } 78 79 // time.Sleep(time.Second * 1) 80 } 81 } 82 func2 := func() { 83 for i := 1; i <= 10; i++ { 84 select { 85 case chan1 <- i: 86 fmt.Printf("chan1 <- %v 成功!\n", i) 87 case chan2 <- fmt.Sprintf("func2:%v", i): 88 fmt.Printf("chan2 <- %v 成功!\n", fmt.Sprintf("func2:%v", i)) 89 default: 90 fmt.Println("func2写数据被堵塞了!") 91 } 92 // time.Sleep(time.Second * 1) 93 } 94 } 95 go func1() 96 go func2() 97 98 time.Sleep(time.Second * 10) 99 100 } 101 102 // 互斥锁实现乘阶 103 func TestMutex() { 104 chengjie := func(n int) { 105 sum := 1 106 for i := 1; i <= n; i++ { 107 sum *= i 108 } 109 lock.Lock() 110 myMap[n] = sum 111 defer lock.Unlock() 112 } 113 for i := 1; i <= 200; i++ { 114 go chengjie(i) 115 } 116 time.Sleep(time.Second * 5) 117 lock.Lock() 118 for i, v := range myMap { 119 fmt.Printf("myMap[%d]=%d\n", i, v) 120 } 121 defer lock.Unlock() 122 } 123 124 // 管道实现乘阶 125 func TestChannel() { 126 /* 127 管道语法: 128 var [chanName] chan [type] 129 使用说明: 130 1.channel是引用类型 131 2.channel必须使用make初始化之后才能使用. 132 3.make()初始化时指定的容量是固定的 133 4.管道为空的时候去读会阻塞,管道已满的时候去写也会阻塞 134 5.当所有协程都处于休眠状态,或者所有协程都退出,而代码仍然要从空管道读取,或者往满了的管道写入都会报错(fatal error: all goroutines are asleep - deadlock!) 135 6.从管道中取数据遵循先进先出的规则 136 7.如果你想让管道能放任意数据类型,可以指定类型为interface{}.只不过取数据之后需要做一次类型断言 137 8.管道关闭之后(Close(chan)内置函数),就不能再往管道里面推数据了,但是可以从管道中读取数据,直到读完. 138 9.如果你要读取第二个数据的话,必须见推出第一个数据 139 10.channel支持for-range遍历,但有两个注意: 140 .在遍历时,如果channel没有关闭,则会出现deadlock的错误 141 .在遍历时,如果channel已经关闭,则会正常遍历数据遍历完成后,就会退出遍历 142 11.默认情况下管道是可读可写的,但你也可以声明只读或者只写的管道 143 只读管道的声明: var chan1 <-chan int 144 只写管道的声明: var chan1 chan<- int 145 只读只写管道的作用: 在传参数的时候,可以直接设定管道是否可读可写,来减少犯错 146 12.可以用select来启用非阻塞读取或写入 147 148 149 */ 150 151 // 创建一个可以放3个int型数据的管道变量 152 var intChan chan int 153 intChan = make(chan int, 3) 154 155 // 往管道写入数据 156 intChan <- 10 157 var ti int = 1 158 intChan <- ti 159 160 // 查看管道的长度和容量 161 fmt.Printf("intChan Type=%v, Value=%v, len=%v, cap=%v \n", intChan, intChan, len(intChan), cap(intChan)) 162 163 // 从管道中取出数据 164 ss := <-intChan 165 fmt.Printf("%v := <-intChan \n", ss) 166 intChan <- 11 167 168 // 关闭管道 169 close(intChan) 170 171 // 遍历管道 172 for val := range intChan { 173 fmt.Printf("%v\t", val) 174 } 175 fmt.Println() 176 177 fmt.Println("================================") 178 var waitb chan bool = make(chan bool, 1) 179 waitb <- false 180 a, ok := <-waitb 181 fmt.Printf("a=%v, b=%v\n", a, ok) 182 waitb <- true 183 a, ok = <-waitb 184 fmt.Printf("a=%v, b=%v\n", a, ok) 185 186 fmt.Println("================================") 187 fmt.Println() 188 189 var cjchan chan int = make(chan int, 200) 190 var mapChan chan map[int]int = make(chan map[int]int, 1) 191 192 for i := 1; i <= 200; i++ { 193 cjchan <- i 194 } 195 close(cjchan) 196 197 cj := make(map[int]int, 200) 198 mapChan <- cj 199 200 chengjie := func(c1 chan map[int]int, c2 chan int, c3 chan bool) { 201 temap := make(map[int]int, 1) 202 for { 203 n, ok := <-c2 204 if !ok { 205 break 206 } 207 sum := 1 208 for i := 1; i <= n; i++ { 209 sum *= i 210 } 211 temap[n] = sum 212 // fmt.Printf("chengjie %v: %v ok=%v\n", n, sum, ok) 213 } 214 imap := <-c1 215 for i, v := range temap { 216 imap[i] = v 217 } 218 c1 <- imap 219 220 if len(imap) == 200 { 221 c3 <- true 222 } 223 } 224 225 // go chengjie(mapChan, cjchan, waitb) 226 for i := 1; i <= 10; i++ { 227 go chengjie(mapChan, cjchan, waitb) 228 } 229 230 <-waitb 231 for i, v := range cj { 232 fmt.Printf("myMap[%d]=%d\n", i, v) 233 } 234 235 } 236 237 func TestSetCPU() { 238 // 获取CPU个数. NumCPU()返回本地机器的逻辑CPU个数 239 cpuNum := runtime.NumCPU() 240 fmt.Println("CPU个数=", cpuNum) 241 // 设置程序执行调度使用CPU个数.默认会自动设置,不需要手动设置 242 runtime.GOMAXPROCS(cpuNum) 243 244 } 245 246 func TestMain() { 247 TestGoroutine := func() { 248 for i := 1; i < 10; i++ { 249 fmt.Println("TestGoroutine() Hello world!", i) 250 time.Sleep(time.Second) 251 } 252 } 253 go TestGoroutine() 254 for i := 1; i < 10; i++ { 255 fmt.Println("TestMain() Hello world!", i) 256 time.Sleep(time.Second) 257 } 258 259 }
锁\原子操作
1 func TestSync() { 2 var lock sync.RWMutex 3 4 lock.Lock() // "写"锁 5 lock.Unlock() // "写"解锁 6 lock.RLock() // "读"锁 7 lock.RUnlock() // "读"解锁 8 9 // 数组\切片\结构体都允许并发修改(但会存在脏写),并发修改map有时候会发生panic 10 // 如果要修改map可以使用sync.Map 11 var a sync.Map 12 a.Store("a", 1) // add操作 13 v, _ := a.Load("a") // read操作 14 fmt.Println(v) 15 16 // 原子操作:并发的时候比如用到i++类似的 17 var i int32 18 atomic.AddInt32(&i, 1) 19 fmt.Println(i) 20 21 // 只执行一次函数 22 var onece sync.Once 23 func1 := func() { 24 onece.Do(func() { 25 fmt.Println("aaaaa") 26 }) 27 } 28 func1() 29 func1() 30 31 }