本文内容纲要:
- 基础
- return、exit、goexit区别
- return
- exit
- goexit
- 多go程通信(channel)
- 管道的注意点
- 管道nil
- 管道死锁
- for range遍历管道
- 判断管道是否已经关闭
- 单向通道
- 管道监听(select)
- 管道总结
目录
- 
基础 
- 
return、exit、goexit区别 - return
- exit
- goexit
 
- 
多go程通信(channel) 
- 
管道的注意点 - 管道nil
- 管道死锁
- for range遍历管道
 
- 
判断管道是否已经关闭 
- 
单向通道 
- 
管道监听(select) 
- 
管道总结 
count := 1
for {
	fmt.Println("我是主go程:", count)
	count++
	time.Sleep(time.Second)
}
- return:返回当前函数
- exit:退出当前进程
- goexit:提前退出当前go程
return
package main
import (
	"fmt"
	"time"
)
func main() {
	go func() {
		func(){
			fmt.Println("这是子go程的内部函数")
			return	// 只是返回当前函数,对于上一层的函数会继续执行
		}()
		fmt.Println("子go程结束")
	}()
	fmt.Println("这是主go程")
	time.Sleep(5 * time.Second)
	fmt.Println("over")
}
exit
package main
import (
	"fmt"
	"os"
	"time"
)
func main() {
	go func() {
		func(){
			fmt.Println("这是子go程的内部函数")
			os.Exit(-1)		// 退出进程
		}()
		fmt.Println("子go程结束")
	}()
	fmt.Println("这是主go程")
	time.Sleep(5 * time.Second)
	fmt.Println("over")
}
goexit
package main
import (
	"fmt"
	"runtime"
	"time"
)
func main() {
	go func() {
		func(){
			fmt.Println("这是子go程的内部函数")
			runtime.Goexit()	// 退出当前go程
		}()
		fmt.Println("子go程结束")
	}()
	fmt.Println("这是主go程")
	time.Sleep(5 * time.Second)
	fmt.Println("over")
}
// 创建两个go程,父母写数据,儿子读数据。
// 这是go程“儿子”,从管道中读取数据
go func() {
	for i := 0; i < 50; i++ {
		data := <-numChan
		fmt.Println("<----这是go程“儿子”,读取数据:", data)
	}
}()
go func() {
	for i := 0; i < 20; i++ {
		// 这是go程“妈妈”,写入20个数据
		numChan <- i
		fmt.Println("---->这是go“妈妈”,写入数据:", i)
	}
}()
for i := 20; i < 50; i++ {
	// 这是go程“爸爸”,写入30个数据
	numChan <- i
	fmt.Println("---->这是go程”爸爸“,写入数据:", i)
}
管道nil
如果管道没有使用make分配空间,那么管道默认为nil,读取写入都会阻塞
package main
import "fmt"
func main() {
	var numChan chan int
	numChan <- 1
	fmt.Println("numChan", <- numChan)
}
管道死锁
当管道读写次数不一致的时候,如果阻塞在主go程,那么程序会崩溃,如果阻塞在子go程,那么会出现内存泄露
package main
import "fmt"
func main() {
	numChan := make(chan int, 10)
	
	// 写入数据到管道
	go func() {
		for i := 0; i < 50; i++ {
			numChan <- i
			fmt.Println("写入数据:", i)
		}
	}()
    
    // 读,当主程序被管道阻塞时,那么程序将锁死崩溃
	for i := 0; i < 60; i++ {
		fmt.Println("numChan:", <-numChan)
	}
}
for range遍历管道
for range是不知道管道是否写完,所以会一直等待,一直等待就会导致死锁
package main
import "fmt"
func main() {
	numChan := make(chan int, 10)
	go func() {
		for i := 0; i < 50; i++ {
			numChan <- i
			fmt.Println("写入数据:", i)
		}
	}()
	// 遍历管道时,只返回值,不返回坐标
	for val := range numChan{
		fmt.Println("读取数据:",val)
	}
}
在写入端,将管道关闭,for range遍历关闭管道(nil)时,会退出就不会导致死锁
package main
import "fmt"
func main() {
	numChan := make(chan int, 10)
	go func() {
		for i := 0; i < 50; i++ {
			numChan <- i
			fmt.Println("写入数据:", i)
		}
		// 手动关闭管道
		close(numChan)
	}()
	for val := range numChan{
		fmt.Println("读取数据:",val)
	}
}
val, ok := numMap[0]ok-idiompackage main
import "fmt"
func main() {
	numChan := make(chan int, 10)
	go func() {
		for i := 0; i < 10; i++ {
			numChan <- i
			fmt.Println("写入数据:", i)
		}
		// 手动关闭管道
		close(numChan)
	}()
	for {
		val, ok := <-numChan
		if ok {
			fmt.Println("读取数据:", val)
		} else {
			fmt.Println("管道已经关闭")
			break
		}
	}
}
// 在主函数中创建一个双向通道
numChan := make(chan int, 10)
// 双向管道可以赋值给同类型的单向管道
// 将numChan,传递给producer,负责生产
go producer(numChan)
// 将numChan,传递给consumer 负责消费
go consumer(numChan)
time.Sleep(5 * time.Second)
channelchan1chan2chan1chan2selectselectswitch casepackage main
import (
	"fmt"
	"time"
)
func main() {
	// 启动一个go程,负责监听两个channel
	chan1 := make(chan int)
	chan2 := make(chan int)
	go func() {
		for {
			select {
			case val := <-chan1:
				fmt.Println("从chan1读取数据成功:", val)
			case val2 := <-chan2:
				fmt.Println("从chan2读取数据成功:", val2)
			}
		}
	}()
	go func() {
		for i := 0; i < 10; i++ {
			chan1 <- i
			time.Sleep(time.Second)
		}
	}()
	go func() {
		for i := 0; i < 10; i++ {
			chan2 <- i
			time.Sleep(time.Second)
		}
	}()
	for{
	}
}
- 
当管道写满了,写阻塞 
- 
当缓冲区读完了,读阻塞 
- 
如果管道没有使用make分配空间,管道默认nil - 从nil管道读取/写入数据,都会阻塞(不会崩溃)
 
- 
从一个已经close的管道读取/写入数据时,会返回零值(不会崩溃) 
- 
一个管道,如果重复关闭,程序会崩溃 
- 
关闭管道的动作,一定要在写管道的操作方执行,不应该放在读端,否则继续写会崩溃 
- 
读写通道次数一定要对等 - 否则在多个go程中,会出现资源泄露
- 在主go程中,会出现程序崩溃(deadlock)
 
本文内容总结:基础,return、exit、goexit区别,return,exit,goexit,多go程通信(channel),管道的注意点,管道nil,管道死锁,for range遍历管道,判断管道是否已经关闭,单向通道,管道监听(select),管道总结,
