go并发编程笔记1
协程与线程的区别
- 协程:用户态,轻量级线程,栈KB级别
- 线程:内核态,线程跑多个协程,栈MB级别
time.Sleep保证在子协程跑完之前,主协程不退出。
并发编程
并发编程之协程
创建协程go task()
package main
import (
"fmt"
"time"
)
func show(msg string) {
for i := 1; i < 5; i++ {
fmt.Printf("msg: %v\n", msg)
time.Sleep(time.Millisecond * 100)
}
}
func main() {
go show("java")
show("golang") // 在main协程中执行,如果它前面也添加go,程序没有输出
fmt.Println("end...")
}
如果有多个协程,主协程结束程序就结束了,会自动杀死其他协程。
可以用time.sleep等
并发编程之通道channel
go提供的一种称为通道的机制,用于在goroutine之间共享数据。当您作为goroutine执行并发活动时,需要在goroutine之间共享资源或数据,通道充当goroutine之间的管道并提供一种机制来保证同步交换。
需要在声明时指定数据类型,我们可以共享内置、命名、结构和引用类型的值和指针。数据在通道上传递:在任何时间只有一个goroutine可以访问数据项:因此不会发生数据竞争。
根据数据交换行为有两种类型的通道:
- 无缓冲通道:用于执行goroutine之间的同步通信,无缓冲通道保证在发送和接受发生的瞬间执行两个goroutine之间的交换。缓冲通道则没有这样的保证。
- 缓冲通道:缓冲通道用于执行异步通道
通道由make函数创建,该函数指定chan关键字和通道的元素类型。
Unbuffered := make(chan int)
buffered := make(chan int,10)
将值发送到通道需要使用<-运算符
goroutine1 := make(chan string,5)
goroutine1 <- "Australia"
一个包含5个值的缓冲区的字符串类型的goroutine1通道。然后我们通过通道发送字符串"Australia"
从通道接收值
data := <-goroutine1//从通道接收字符串
<-字符串附加到变量goroutine的左侧,以接收来自通道的值。
通道的发送和接收特性
1.对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的。
2.发送操作和接收操作中对元素值的处理都是不可分割的。
3.发送操作在完全完全之前会被堵塞。接收操作也是如此。
并发编程之WaitGroup实现同步
wg.Done() // goroutine结束就登记-1
wg.Add(1) // 启动一个goroutine就登记+1
等于零了就退出
package main
import "fmt"
var wg sync.WaitGroup
func hello(i int) {
defer wg.Done() // goroutine结束就登记-1
fmt.Println("Hello Goroutine!", i)
}
func main() {
for i := 0; i < 10; i++ {
wg.Add(1) // 启动一个goroutine就登记+1
go hello(i)
}
wg.Wait() // 等待所有登记的goroutine都结束
}
并发编程之runtime包
runtime.Gosched() 让出cpu时间片,重新等待安排任务,让给子协程先运行
runtime.Goexit() 退出当前协程
runtime.GOMAXPROCS() 设置最大的cpu核心数,默认为最大
并发编程之Mutex互斥锁实现同步
除了使用channel实现同步之外,还可以使用Mutex互斥锁的方式实现同步。
package main
import(
"fmt"
"sync"
"time"
)
var m int =100
var lock sync.Mutex
var wt sync.WaitGroup
func add(){
defer wt.Done()
lock.Lock()
m +=1
time.Sleep(time.Millisecond *10)
lock.Unlock()
}
func sub(){
defer wt.Done()
lock.Lock()
time.Sleep(time.Millisecond *2)
m -=1
lock.Unlock()
}
func main(){
for i:=0;i<100;i++{
go add()
wt.Add(1)
go sub()
wt.Add(1)
}
wt.Wait()
fmt.Printf("m:%v\n",m)
}
加锁后一个进程运行其他的是进不来的