并发,并行,串行 协程 概念
并发多个线程交替执行
并行 两个程序同时运行,多核多cpu 不一定快,如果两个代码要通信第一个线程要拿到第二个的结果必须等第二个线程把结果返回,通信开销大
串行 按部就班从上到下顺序执行
协程俗称轻量级的线程,协程调用完函数返回一个接口
Goroutine如果main的Goroutine终止了,程序将被终止,而其他Goroutine将不会运行
Goroutine
func main() {
//获取goroot目录
fmt.Println(runtime.GOROOT())
fmt.Println(runtime.GOOS)
fmt.Println(runtime.NumCPU())
//go调度
go func() {
for i := 0; i < 100; i++ {
fmt.Println("goroutine", i)
}
}()
for i := 0; i < 100; i++ {
//让时间片,让别人先执行
runtime.Gosched()
fmt.Println("main", i)
}
}
Gorotine调度与终止
func main() {
go func() {
fmt.Println("start")
text()
fmt.Println("end")
}()
time.Sleep(time.Second * 2)
}
func text() {
defer fmt.Println("test defer")
//return 终止函数
//终止协程 runtime.Goexit()
fmt.Println("test")
}
临界资源安全问题
// 在并发编程中对临界资源处理不当,往往导致数据不一致的问题
func main() {
a := 1
go func() {
a = 2
fmt.Println("goroutine,a=", a)
}()
a = 3
time.Sleep(time.Second * 2)
fmt.Println("main,a=", a)
}
Mutex锁和 Waitgroup
var mutex sync.Mutex
var ticket int = 10
var wg1 sync.WaitGroup
func main() {
wg1.Add(4)
go saleTickets("售票口1")
go saleTickets("售票口2")
go saleTickets("售票口3")
go saleTickets("售票口4")
wg1.Wait()
}
//售票函数
func saleTickets(name string) {
defer wg1.Done()
for {
//检查之前上锁
mutex.Lock()
if ticket > 0 {
fmt.Println(name, "剩余票数", ticket)
ticket--
} else {
mutex.Unlock()
fmt.Println("票卖光了")
break
}
mutex.Unlock()
}
}
channel通道讲解
func main() {
//定义一个bool类型的通道
var ch chan bool
ch = make(chan bool)
go func() {
for i := 0; i < i; i++ {
fmt.Println(i)
}
ch <- true
}()
//读取通道中的数据
data := <-ch
fmt.Println("读取到通道中的信息,ch=", data)
}
关闭通道
func main() {
ch := make(chan int)
go test3(ch)
//通道中读数据
for {
//time.Sleep(time.Second)
v, ok := <-ch
if !ok {
fmt.Println("数据读取完毕,通道已经关闭 ok=", ok)
break
}
fmt.Println("通道中读取的数据", v)
}
//狂总推荐用for-range
//for v := range ch {
// fmt.Println("通道中读取的数据", v)
//}
}
func test3(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
缓冲通道
func main() {
ch1 := make(chan int)
fmt.Println(len(ch1), cap(ch1))
ch2 := make(chan int, 5)
fmt.Println(len(ch2), cap(ch2))
ch2 <- 2
fmt.Println(len(ch2), cap(ch2))
ch3 := make(chan string, 4)
go test4(ch3)
for v := range ch3 {
time.Sleep(time.Second)
fmt.Println(v)
}
}
func test4(ch chan string) {
for i := 0; i < 10; i++ {
ch <- "test" + strconv.Itoa(i)
fmt.Println("向缓冲区写入的数据", "test"+strconv.Itoa(i))
}
close(ch)
}
0 0
0 5
1 5
向缓冲区写入的数据 test0
向缓冲区写入的数据 test1
向缓冲区写入的数据 test2
向缓冲区写入的数据 test3
向缓冲区写入的数据 test4
test0
向缓冲区写入的数据 test5
test1
向缓冲区写入的数据 test6
test2
向缓冲区写入的数据 test7
test3
向缓冲区写入的数据 test8
test4
向缓冲区写入的数据 test9
test5
test6
test7
test8
test9
定向通道
func main() {
//ch1 := make(chan<- int)
//ch1 <- 200
//temp := <-ch1 //cannot receive from send-only channel ch1 (variable of type chan<- int)
//ch2 := make(<-chan int)
//ch2 <- 200 //cannot send to receive-only channel ch2 (variable of type <-chan int)
ch1 := make(chan int)
go writeOnly(ch1)
go readOnly(ch1)
time.Sleep(time.Second)
}
// 只写通道
func writeOnly(ch chan<- int) {
ch <- 100
}
func readOnly(ch <-chan int) {
data := <-ch
fmt.Println(data)
}
Select 随机选择通道中的数据
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
time.Sleep(time.Second)
ch1 <- 100
}()
go func() {
time.Sleep(time.Second)
ch2 <- 200
}()
select {
case num1 := <-ch1:
fmt.Println("ch1", num1)
case num2 := <-ch2:
fmt.Println("ch2", num2)
//default:
//fmt.Println("没有匹配到")
定时器
func main() {
//创建一个定时器
timer := time.NewTimer(3 * time.Second)
fmt.Println(time.Now())
//获取定时器的通道
timerChan := timer.C
//把定时器中的数据读出来
fmt.Println(<-timerChan)
fmt.Println("-------------------")
//怎么手动关掉定时器
timer2 := time.NewTimer(5 * time.Second)
go func() {
<-timer2.C //5s
fmt.Println("子goroutine end")
}()
time.Sleep(time.Second * 2)
stop := timer2.Stop()
if stop {
fmt.Println("timer2停止了")
}
}
定时器第二个案例在指定时间触发函数执行
func main() {
timeChan := time.After(time.Second * 3)
fmt.Println(time.Now())
chantime := <-timeChan
fmt.Println(chantime)
//在指定时间触发这个函数执行,备份
time.AfterFunc(time.Second*3, test10)
time.Sleep(time.Second * 5)
}
func test10() {
fmt.Println("aaaaaa")
}