go并发编程笔记1|青训营

63 阅读3分钟

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)
	
}

加锁后一个进程运行其他的是进不来的