Go语言学习笔记(七)并发

1.线程模型:

M:1模型:所有的用户线程对应一个内核线程.

1:1模型:一个用户线程对应一个内核线程.

M:N模型:多对多模型,M个用户线程对应N个内核线程.go语言协程就是M:N的一种表现.

2.协程的使用:

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {

	go fmt.Println("启动新的阶段")

	time.Sleep(time.Second)
}

2.1执行结果:

3.Go语言协程同步:

3.1独占锁-Mutex:

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {

	go func() {
		for i := range 5 {
			//模拟耗时操作.
			time.Sleep(time.Second)
			fmt.Println(i)
		}
	}()

	go func() {
		for i := range 5 {
			time.Sleep(time.Second)
			fmt.Println(i * 10)
		}
	}()

	time.Sleep(time.Second * 10)
}

3.2执行结果:

可以看到结果执行是交叉乱序输出的.可以使用sync.Mutex互斥来实现顺序输出.

3.3独占锁-Mutex:

go 复制代码
package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {

	//定义一个独占锁.
	lock := sync.Mutex{}
	go func() {
		//加锁.
		lock.Lock()
		//解锁
		defer lock.Unlock()
		for i := range 5 {
			//模拟耗时操作.
			time.Sleep(time.Second)
			fmt.Println(i)
		}
	}()

	go func() {
		//加锁.
		lock.Lock()
		//解锁
		defer lock.Unlock()
		for i := range 5 {
			time.Sleep(time.Second)
			fmt.Println(i * 10)
		}
	}()

	time.Sleep(time.Second * 10)
}

3.4执行结果:

4.读写锁:

go 复制代码
package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {

	//创建读写锁.
	lock := sync.RWMutex{}
	go func() {
		//加锁.
		lock.RLock()
		//解锁
		defer lock.RUnlock()
		for i := range 5 {
			//模拟耗时操作.
			time.Sleep(time.Second)
			fmt.Println(i)
		}
	}()

	go func() {
		//加锁.
		lock.RLock()
		//解锁
		defer lock.RUnlock()
		for i := range 5 {
			time.Sleep(time.Second)
			fmt.Println(i * 10)
		}
	}()

	time.Sleep(time.Second * 10)
}

4.2执行结果:

从结果可以看出,数据是交叉进行输出.读锁不会进行互斥.

4.3读写锁互斥:

如果一个协程尝试获取读锁,另一个尝试获取写锁,两个锁也会进行互斥.

go 复制代码
package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {

	//创建写锁.
	lock := sync.RWMutex{}
	go func() {
		//加锁.
		lock.RLock()
		//解锁
		defer lock.RUnlock()
		for i := range 5 {
			//模拟耗时操作.
			time.Sleep(time.Second)
			fmt.Println(i)
		}
	}()

	go func() {
		//加锁.
		lock.Lock()
		//解锁
		defer lock.Unlock()
		for i := range 5 {
			time.Sleep(time.Second)
			fmt.Println(i * 10)
		}
	}()

	time.Sleep(time.Second * 10)
}

4.4执行结果:

******从执行结果可以看出,读写锁进行了互斥.

从执行结果可以看出,读写锁进行了互斥.

5.等待组-WaitGroup:

scss 复制代码
package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {

	//等待组对象.
	wait := sync.WaitGroup{}
	//计数器加2.
	wait.Add(2)
	go func() {
		for i := range 5 {
			//模拟耗时操作.
			time.Sleep(time.Second)
			fmt.Println(i)
		}
		//计数器减1.
		wait.Done()
	}()

	go func() {
		for i := range 5 {
			time.Sleep(time.Second)
			fmt.Println(i * 10)
		}
		wait.Done()
	}()

	//阻塞主协程直到计数器为0.
	wait.Wait()
}

5.1执行结果:

从结果可以看出,主协程等到所有子协程完成任务之后才结束.

6.channel实现互斥:

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {

	//创建一个缓冲区大小为1的channel.
	c := make(chan int, 1)

	//写入元素.
	c <- 0
	go func() {
		<-c
		for i := range 5 {
			//模拟耗时操作.
			time.Sleep(time.Second)
			fmt.Println(i)
		}
		c <- 0
	}()

	go func() {
		<-c
		for i := range 5 {
			time.Sleep(time.Second)
			fmt.Println(i * 10)
		}
		c <- 0
	}()

	time.Sleep(time.Second * 10)
}

6.1执行结果:

从结果可以看出实现了互斥.

6.2channel实现协程依赖:

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {

	//创建一个缓冲区大小为1的channel.
	c := make(chan int, 1)
	go func() {
		<-c
		for i := range 5 {
			//模拟耗时操作.
			time.Sleep(time.Second)
			fmt.Println(i)
		}
	}()

	go func() {
		for i := range 5 {
			time.Sleep(time.Second)
			fmt.Println(i * 10)
		}
		c <- 0
	}()

	time.Sleep(time.Second * 10)
}

6.3执行结果:

从执行结果可以看出,两个协程产生了依赖关系,执行了另一个协程下一个协程才可以继续执行.

6.4channel实现等待组:

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {

	count := 2
	//创建一个缓冲区大小为1的channel.
	c := make(chan int, 1)

	go func() {
		for i := range 5 {
			//模拟耗时操作.
			time.Sleep(time.Second)
			fmt.Println(i)
		}
		c <- 0
	}()

	go func() {
		for i := range 5 {
			time.Sleep(time.Second)
			fmt.Println(i * 10)
		}
		c <- 0
	}()

	for i := 0; i < count; i++ {
		<-c
	}

}

6.5执行结果:

从执行结果可以看出,达到了waitGroup的效果.

7.timeSleep和runtime.Gosched区别:

time.Sleep:是通过休眠来达到让出cpu执行权.

runtime.Gosched:是主动让出cpu的执行权.像java中yield方法一样.

8.Go单例:

go 复制代码
package main

import (
	"fmt"
	"sync"
	"time"
)

// 单例数据结构.
type SingleObject struct {
}

// sync.once实例.
var one sync.Once

// 单例对象.
var instance *SingleObject

func getInstance() *SingleObject {
	one.Do(func() {
		//为全局变量赋值.
		instance = &SingleObject{}
		fmt.Println("实例化SingleObject")
	})
	return instance
}
func main() {

	for i := 0; i < 10; i++ {
		go getInstance()
	}
	time.Sleep(time.Second * 10)

}

8.1执行结果:

从结果可以看出这个方法只执行了一次.从而达到了单例的目的.

语雀地址www.yuque.com/itbosunmian...?

《Go.》 密码:xbkk 欢迎大家访问.提意见.

相关推荐
神奇小汤圆29 分钟前
解放双手!SpringBoot 6种自动填充公共字的手段,这些代码真没必要手写!
后端
鱼人40 分钟前
多态底层原理:向上转型、动态绑定,一文彻底搞懂
后端
神奇小汤圆43 分钟前
吃透Redis缓存核心:淘汰策略、LRU/LFU区别与四大缓存问题全解
后端
无责任此方_修行中1 小时前
搓了一个国产大模型与 AI Agent 比价工具
前端·后端·ai编程
yunyi2 小时前
改个 SSH 端口,我把自己锁在了服务器外面
运维·后端
元界metalite2 小时前
MyBatis多数据源常见难题-事务开始了为何数据源还没选出来
后端·mybatis
orient2 小时前
ForkJoin 框架源码深度解析:工作窃取 + RecursiveTask 实战
后端
karry_k2 小时前
Skill 到底是什么?从提示词、脚本到多智能体工作流的完整指南
java·人工智能·后端
人间凡尔赛3 小时前
eBPF + WebAssembly 正在重写服务网格数据平面:2026 云原生架构的“去 Sidecar“革命
后端·云原生·架构