一、线程等待
type WaitGroup结构体(Add、Done、Wait):
package main
import (
"fmt"
"sync"
)
func mainWait() {
for i := 1; i < 10; i++ {
fmt.Println("main:", i)
}
}
func TestWait(wg *sync.WaitGroup) {
defer wg.Done() //减少信号次数
for i := 1; i < 100; i++ {
fmt.Println("Route:", i)
}
}
func main() {
var wg sync.WaitGroup //设置信号量
wg.Add(1) //加上信号次数(次数用完Wait方法阻塞等待的所有线程都会释放)
go TestWait(&wg)//这里一定要传指针,不然无法修改wg的值,线程一直等待会死锁
mainWait()
wg.Wait() //等待信号结束
}
注:Wait和Add要在例程外面,Done在例程里面,不然容易直接结束线程等待
二、互斥锁
背景:多个例程对同一数据进行操作,需要加锁,类似Redis线程锁
type Mutex结构体(Lock,Unlock)
package main
import (
"fmt"
"sync"
)
func main() {
var a int = 2000
var b int = 2000
var wg sync.WaitGroup
var locker sync.Mutex //定义锁变量
wg.Add(2)
go func() {
locker.Lock() //加锁
a = a + 1000
b = b - 1000
wg.Done()
locker.Unlock() //解锁,可以使用defer,但是需要匿名函数
}()
go func() {
locker.Lock()
a = a - 1000
b = b + 1000
wg.Done()
locker.Unlock()
}()
wg.Wait()
fmt.Printf("总共为:%d", a+b)
}
三、sync/atomic原子性操作
1、底层加锁原理;
2、具体怎么使用查看官方文档;
3、看为一个整体操作
四、管道
具体知识前往Go语言基础管道,下面只讲解部分知识点
1、无缓冲区管道读写
写和读的操作必须成对,不然会进行阻塞
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan struct{
})
go func() {
time.Sleep(5 * time.Second) //设置等待,5s后才能写入
ch <- struct{
}{
} //空结构体可以节约内存
}()
<-ch //主线程阻塞,一直等待线程写入后读取
fmt.Println("end")
}
2、关闭管道
管道关闭后无法继续写入
close(ch) //关闭管道
ch <- 1
Panic错误(可以恢复的错误):
panic: send on closed channel
3、阻塞发生情况
阻塞和死锁不同,死锁是没人写,阻塞是读写不同步
4、监听管道
package main
import (
"fmt"
"time"
)
func main() {
var ch1 chan struct{
} = make(chan struct{
})
var ch2 chan struct{
} = make(chan struct{
})
go func() {
time.Sleep(4 * time.Second)
ch1 <- struct{
}{
}
}()
go func() {
time.Sleep(3 * time.Second)
ch2 <- struct{
}{
}
}()
select {
case <-ch1:
fmt.Println("This is ch1")
case <-ch2:
fmt.Println("This is ch2")
}
}
实例,main函数超过5秒就输出超时,没5秒就输出
第二种方法:
package main
import (
"fmt"
_ "fmt"
"time"
)
func main() {
res := make(chan time.Time)
go func() {
time.Sleep(6 * time.Second)
res <- time.Now()
}()
select {
case <-res:
fmt.Println("任务成功")
case <-time.After(5 * time.Second):
fmt.Println("已经超时")
}
}
五、读写锁
作用;并发的修改同一文件数据(读取数据不用),因为互斥锁会把所有操作都锁起来,包括读的操作,会导致效率下降,所以出现了读写锁。
//读写锁
//获取锁 Lock Rlock
//释放锁 Unlock Runlock
//写写互斥,写读互斥,读读不互斥(Rlock,Rlock)
package main
import (
"fmt"
_ "fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
var locker sync.RWMutex
wg.Add(2)
go func() {
fmt.Println("A before")
locker.RLock()
fmt.Println("A Do")
time.Sleep(10 * time.Second)
locker.RUnlock()
fmt.Println("A After")
wg.Done()
}()
go func() {
time.Sleep(time.Second * 1)
fmt.Println("B before")
locker.RLock()
fmt.Println("B Do")
locker.RUnlock()
fmt.Println("B After")
wg.Done()
}()
wg.Wait()
}
六、Cond锁
//type Cond 对象的使用()
package main
import (
"fmt"
_ "fmt"
"sync"
"time"
)
func main() {
var locker sync.Mutex
var wg sync.WaitGroup
wg.Add(2)
cond := sync.NewCond(&locker) //创建cond结构体指针
go func() {
cond.L.Lock()
cond.Wait() //起到了阻塞作用
fmt.Println("我得到信号了")
wg.Done()
cond.L.Unlock()
}()
go func() {
time.Sleep(time.Second * 5)
wg.Done()
cond.Broadcast()
}()
wg.Wait()
}
七、Once
没有就直接创建池对象,有就直接用
package main
import (
"fmt"
_ "fmt"
"sync"
)
type student struct {
id int
}
func main() {
index := 0
pool := sync.Pool{
New: func() interface{
} {
index++
stu := student{
id: index,
}
return &stu
},
}
c := pool.Get()
fmt.Println(c) //pool中创建c
c1 := pool.Get()
fmt.Println(c1) //pool中创建c1
pool.Put(c1) //把c1放回去
c2 := pool.Get() //再拿的时候pool中有c1,所以直接取c1
fmt.Println(c2)
}
九、Runtime
Gosched调度获取
package main
import (
"fmt"
_ "fmt"
"runtime"
"sync"
)
func main() {
runtime.Gosched() // 让出CPU时间 time.Sleep()
// var wg *sync.WaitGroup
wg := new(sync.WaitGroup)
for i := 0; i < 2; i++ {
wg.Add(1)
go func(i int) {
for ch := 'A'; ch < 'Z'; ch++ {
fmt.Printf("%d: %c\n", i, ch)
runtime.Gosched()
}
wg.Done()
}(i)
}
wg.Wait()
}
如果看完对自己有所帮助,请点赞支持。谢谢大家