管道和go程

goroutine(go程)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import (
   "fmt"
   "time"
)

func display(num int) {
   count := 1
   for {
      fmt.Println("============> 这是子go程:", num, "当前count值", count)
      count++
   }

}

func main() {
   // 启动子go程
   for i := 0; i < 3; i++ {
      go display(i)
   }

   // 主go程
   count := 1
   for {
      fmt.Println("============> 这是主go程:", count)
      count++
      time.Sleep(1 * time.Second)
   }

}

提前退出go程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
   "fmt"
   "runtime"
   "time"
)

// GOEXIT ===> 提前退出go程
// return ===> 返回当前函数
// exit ===> 退出当前进程

func main() {
   go func() {
      func() {
         fmt.Println("子go程内部的函数!")
         //return // 退出当前函数
         //os.Exit(-1) // 退出进程
         runtime.Goexit() // 退出当前go程
      }()

      fmt.Println("子go程结束!")
   }()

   // 主go程需要等待子go程退出
   fmt.Println("主go程~")
   time.Sleep(5 * time.Second)
   fmt.Println("OVER!")
}

无缓冲管道

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package main

import (
   "fmt"
   "time"
)

/*
当通道在多个协程之间传输的是指向数据的指针是,且读写操作是由不同的协程操作,则需要提供额外的同步动作。
*/
func main() {
   // 当涉及到多go程时,c语言使用互斥量,上锁来保持资源同步,避免资源竞争问题
   // go语言更好的解决方案是管道、通道
   // 使用通道不需要手动进行加锁
   //sync.RWMutex{}

   // 创建管道 关键字 chan
   numChan := make(chan int) // 装数字的管道,无缓冲通道,未声明空间
   //numChan := make(chan int, 10) // 有缓冲通道

   // 创建两个go程,父写,子读
   // 发现子go程没有发生资源抢夺

   // 子go程1
   go func() {
      for i := 0; i < 25; i++ {
         // 只能 <- 数据流向
         data := <-numChan
         fmt.Println("子go程1 读取data", data)
      }
   }()
   // 子go程2
   go func() {
      for i := 0; i < 25; i++ {
         data := <-numChan
         fmt.Println("子go程2 读取data", data)
      }
   }()

   // 父go程
   for i := 0; i < 50; i++ {
      // 向管道中写入数据
      numChan <- i
      fmt.Println("====> 主go程,写入数据", i)
   }

   time.Sleep(5 * time.Second)

}

有缓冲管道

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import "fmt"

/* 当管道的读写次数不一致的时候
1.如果阻塞在主go程,程序会崩溃
2.如果阻塞在子go程,会发生内存泄露
*/
func main() {
   // 当缓冲写满的时候,写阻塞,被读取后,在恢复写入
   // 当缓冲区读取完毕,读阻塞,开始写入
   // 如果没有使用make分配空间,那么管道默认nil的,读取,写入都会阻塞
   numChan := make(chan int, 10) // 有缓冲通道

   // 子go程1
   go func() {
      for i := 0; i < 25; i++ {
         // 只能 <- 数据流向
         data := <-numChan
         fmt.Println("子go程1 读取data", data)
      }
   }()

   // 父go程
   for i := 0; i < 50; i++ {
      // 向管道中写入数据
      numChan <- i
      fmt.Println("====> 主go程,写入数据", i)
   }

   var names chan string
   // 因为names是nil的,写操作会一直阻塞在这里
   // 并发生 deadlock 的 error
   names <- "luenci"
   fmt.Println("names", <-names)
}

for range读取管道

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

func main() {

   numChan := make(chan int)

   go func() {
      for i := 0; i < 50; i++ {
         numChan <- i
         fmt.Println("写入数据<<", i)
      }
      fmt.Println("数据写入完成,关闭管道")
      // 从一个close的管道中读取数据时,会返回零值(不会崩溃)
      close(numChan)
   }()

   // 遍历一个管道时,只会返回一个值
   for v := range numChan {
      fmt.Println("读取数据>>", v)
   }

}

判断管道是否关闭

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"

func main() {
   numChan := make(chan int, 10)

   go func() {
      for i := 0; i < 10; i++ {
         numChan <- i
      }
      close(numChan)
   }()

   for {
      // ok-idom 模式判断
      if v, ok := <-numChan; ok {
         fmt.Println("读取数据", v)
      } else {
         fmt.Println("管道已经关闭!")
         break
      }
   }

}

单向管道

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package main

import (
   "fmt"
   "sync"
)

func main() {

   wg := sync.WaitGroup{}

   // 单向通道:为了明确语义,一般用于函数参数
   // 单向读通道:
   //var numChanReadOnly <-chan int
   // 单向写通道:
   //var numChanWriteOnly chan<- int

   //numChanReadOnly = make(chan int, 10)
   //numChanWriteOnly = make(chan int, 10)

   // 双向管道可以赋值给单向管道,单向不能转双向
   numChan := make(chan int, 10) // 双向管道

   // 生产者消费者模型
   wg.Add(1)
   go producer(numChan, &wg)
   wg.Add(1)
   go consumer(numChan, &wg)

   wg.Wait()

}

// producer :生产者  ===> 提供一个只写通道

func producer(in chan<- int, wg *sync.WaitGroup) {

   for i := 0; i < 10; i++ {
      in <- i
      //data:= <-out 写通道不允许读操作
      fmt.Println("======> 向管道中写入数据:", i)
   }
   close(in)
   wg.Done()

}

// consumer :消费者  ===> 只提供一个只读通道

func consumer(out <-chan int, wg *sync.WaitGroup) {

   //out <-10 读通道不允许有写入数据
   for v := range out {
      fmt.Println(v, "<======= 从管道中读取数据")
   }
   wg.Done()
}

利用管道控制go程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import "fmt"

func main() {
   c := make(chan bool, 100)
   for i := 0; i < 100; i++ {
      go func(i int) {
         fmt.Println(i)
         c <- true
      }(i)
   }

   for i := 0; i < 100; i++ {
      <-c
   }
}

select用法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package main

import (
   "fmt"
   "sync"
)

func main() {
   /* 当程序中有多个channel协同工作,ch1,ch2,某一时刻,ch1或者ch2触发了,程序要做响应处理
   1.使用 select 来监听多个管道,当管道被触发时(写入数据,读取数据,关闭管道)
   2.select 语法于 switch case 很像,但是所有的分支条件必须是通道I/O
   */
   var ch1, ch2 chan int
   ch1 = make(chan int)
   ch2 = make(chan int)

   wg := sync.WaitGroup{}
   wg.Add(3)

   // 启动一个go程,负责监听两个channel
   go func() {
      fmt.Println("开始监听....")
      for {
         select {
         case data1 := <-ch1:
            fmt.Println("从ch1中读取数据: ", data1)
         case data2 := <-ch2:
            fmt.Println("从ch2中读取数据: ", data2)
         }
      }
      wg.Done()
   }()

   go func() {
      for i := 0; i < 10; i++ {
         ch1 <- i
      }
      wg.Done()
   }()

   go func() {
      for i := 0; i < 10; i++ {
         ch2 <- i
      }
      wg.Done()
   }()

   wg.Wait()
}