Go并发
goroutinethread

同义说明

goroutinego程轻量级线程contextctx上下文
Go并发基础

主线程与go程

go程是依附于主线程的, 这个依主要附体现在与主线程共存亡, 也就是说主线程一旦结束, go程无论是否执行完毕都会被结束掉。刚接触并发时非常容易出现没有调节好主线程的结束时间,导致go程还未运行结束就被迫结束的问题。
在这里插入图片描述

package main

import (
	"fmt"
)

func 测试线程(起始, 结束 int) {
	for i := 起始; i < 结束; i++ {
		fmt.Println("*-*: ", i)
	}
	fmt.Println("--------------分割线--------------")
}

func main() {
	for i := 0; i < 3; i++ {
		go 测试线程(0, 2)
	}
	测试线程(10, 12)
}

在这里插入图片描述
如上代码, 从执行结果可以看出, 我们的go程只运行了两次,就会因为主进程的结束而被迫结束。(多次执行结果都会不同, 甚至可能会出现go程一次都未执行的情况)
想要解决这个问题最直接的方法,就是让主线程停下来,等等其他线程,其中最笨拙的方法无疑是直接使用睡眠函数,让线程沉睡一会。

func main() {
	for i := 0; i < 3; i++ {
		go 测试线程(0, 2)
	}
	测试线程(10, 12)
	time.Sleep(1000 * time.Millisecond)
}
同步(sync)
sync.WaitGroupAdd(数量)Done()Wait
package main

import (
   "fmt"
   "sync"
)

var 等待组 sync.WaitGroup

func 测试线程(起始, 结束 int, go程 bool) {
   if go程 {
   	defer 等待组.Done()
   }
   for i := 起始; i < 结束; i++ {
   	fmt.Println("*-*: ", i)
   }
   fmt.Println("--------------分割线--------------")
}

func main() {
   for i := 0; i < 3; i++ {
   	等待组.Add(1)
   	go 测试线程(0, 2, true)
   }
   测试线程(10, 12, false)
   等待组.Wait()
}

在这里插入图片描述
这样无论运行多少次, 主线程都会等待go程运行结束后在结束整个进程

管道(channel)

并发中还有一个让人十分头疼的问题,就是不同线程之间的变量传参问题, 解决这个问题, 主要会用到Channel(管道), 这是Go中的一个核心类型其地位类似数组(slice), 散列(map)这样的类型,使用非常便捷。

// 创建管道(无缓存)
var 管道 = make(chan 类型)
var 字符管道 = make(chan string)
var 列表管道 = make(chan []string)
// 创建管道(有缓存)
var 缓存整数管道 = make(chan int, 5)
fatal error: all goroutines are asleep - deadlock!
//管道可以传入任何类型, 包括管道, 比如: <-chan(<-chan string)
func 接收管道(字符 <-chan string) {
	for {
		fmt.Printf("从管道传入了一个:%v\n", <-字符)
	}
}

// 这里如果使用  chan string  则代表为双向管道既可以接收值又可以发送 
func 发送管道(字符 chan<- string) {
	字符 <- "寻觅的"
}

func main() {
	var 管道 = make(chan string)
	go 接收管道(管道)
	发送管道(管道)
	管道 <- "觅库"
	time.Sleep(100 * time.Millisecond)
}

在这里插入图片描述

make(chan 类型, 缓冲大小)
func 双向管道(列表 chan []string) {
	列表 <- []string{"测试1", "测试2"}
	fmt.Println("输出双向管道", <-列表)
}

func main(){
	var 列表管道 = make(chan []string, 1)
	go 双向管道(列表管道)
	time.Sleep(1000 * time.Millisecond)
}

在这里插入图片描述

关闭管道

close()
func 双向管道(列表 chan []string) {
	列表 <- []string{"测试3", "测试4"}
	fmt.Println("输出双向管道", <-列表)

	//列表 <- []string{"测试1", "测试2"}
	close(列表)
	// 这样写可以用来检测当前管道中是否有值, ok为flase的条件是管道中没有值,切执行了close(缺一不可)
	测试, ok := <-列表
	fmt.Println(ok)
	if ok {
		fmt.Println(测试, ok)
	}
}

func main(){
	var 列表管道 = make(chan []string, 1)
	go 双向管道(列表管道)
	time.Sleep(1000 * time.Millisecond)
}
panic: send on closed channel