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 欢迎大家访问.提意见.

相关推荐
Soofjan1 小时前
其它(5):Bleve 全文检索
后端
智碳未来科技有限公司1 小时前
工业双碳实践:基于 SpringBoot + 若依的智碳能源管理系统(zhitan-ems)源码深度解析与落地实战
spring boot·后端·能源
biubiubiu07061 小时前
SpringBoot生产级日志配置
java·spring boot·后端
碎碎念_4921 小时前
SpringBoot和MyBatis框架·速通版
spring boot·后端·mybatis
秋天的一阵风1 小时前
AGENTS.md:你的AI代码助手,需要一份"项目说明书"
前端·后端·ai编程
jserTang1 小时前
手撕 Claude Code-7:自动压缩与记忆恢复
前端·后端
嘟嘟07172 小时前
从 TypeScript 到 Bun:一份前端开发者的效率进阶笔记
后端
用户7508837061952 小时前
循环依赖加 @Lazy 后异常漂移?Spring 三级缓存为什么没兜住?
后端