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 }

浙公網安備 33010602011771號