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

相关推荐
咖啡八杯6 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式
掘金码甲哥7 小时前
这块终端神器, 必须吹爆!
后端
糖果店的幽灵7 小时前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents
Csvn8 小时前
📊 SQL 入门 Day 8:集合操作 — 用 SQL 做数学里的"并交差"
后端·sql
码事漫谈10 小时前
告别数据孤岛与AI“水土不服”:金仓多模融合时序库如何让数据真正服务于业务
后端
IT_陈寒10 小时前
Redis的持久化配置把我坑惨了:你以为数据安全了?
前端·人工智能·后端
星栈10 小时前
Node 接口该写同步还是异步?
后端·node.js
红烧大青虫11 小时前
HarmonyOS应用开发实战:小事记 - UIAbility 的冷启动/热启动/后台启动三种场景与 launchParam 解析
后端·华为·harmonyos·鸿蒙系统
码事漫谈11 小时前
AI Token 缓存:命中省 10 倍,不命中白扔钱
后端
65岁退休Coder11 小时前
LangChain v1.3.4 笔记 - 04 Agent 中间件
后端