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