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 }

 

posted @ 2023-09-18 13:27  看一百次夜空里的深蓝  阅读(28)  评论(0编辑  收藏  举报