概述
打包发送
通往管道的数据如果一次打包发送的性能要高于多次发送。请看下面里面,这个里面我往管道里面发送50000000次
func main() {
done ,c :=make(chan int),make(chan int ,500)
go func() {
count :=0
for x := range c {
count +=x
}
close(done)
}()
for i :=0 ;i<50000000;i++{
c<-i
}
close(c)
<- done
}
结果如下:
time go run mutilsend.go
1249999975000000
real 0m4.705s
user 0m6.666s
sys 0m1.319s
而当我使用数组打包发送,如下:
package main
func main() {
done ,c :=make(chan int),make(chan [500]int ,500)
flag :=0
send :=[500]int{}
go func() {
count :=0
for x := range c {
for _,y :=range x {
count +=y
}
}
println(count)
close(done)
}()
for i :=0 ;i<50000000;i++{
send[flag] = i
if flag ==499 {
flag=0
c<-send
}else{
flag +=1
}
}
close(c)
<- done
}
结果是:
time go run mutilsend.go
1249999975000000
real 0m0.209s
user 0m0.244s
sys 0m0.053s
效率得到了很大的提升。建议搭建在数据量比较大的时候打包发送数据。
锁复制
无论是哪种编程语言,锁对象一定得是公用的,所以的协程是应为共享同一个锁而导致阻塞的,这一点非常重要,下面演示结构体里面使用锁,直接使用会产生锁复制
import (
"sync"
"time"
)
type person struct {
sync.Mutex
}
func (p person) test(act string) {
p.Lock()
defer p.Unlock()
for i:=1;i<10; i++ {
println(act,i)
time.Sleep(time.Millisecond*100)
}
}
func main() {
var p person
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
p.test("read")
}()
go func() {
defer wg.Done()
p.test("write")
}()
wg.Wait()
}
结果如下:
write 1
read 1
read 2
write 2
write 3
read 3
read 4
write 4
read 5
write 5
write 6
read 6
write 7
read 7
read 8
write 8
write 9
read 9
在write的方法没有释放之前,read就使用了这个test,test这个应该函数是不能被其它地方使用的,这里面问题就在锁复制,如何解决这个问题呢,很简单,指针应用
package main
import (
"sync"
"time"
)
type person struct {
sync.Mutex
}
func (p *person) test(act string) {
p.Lock()
defer p.Unlock()
for i:=1;i<10; i++ {
println(act,i)
time.Sleep(time.Millisecond*100)
}
}
func main() {
var p =new(person)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
p.test("read")
}()
go func() {
defer wg.Done()
p.test("write")
}()
wg.Wait()
}
这个里面通过指针的方式避免lock 复制,大家共享同一个锁,看结果
write 1
write 2
write 3
write 4
write 5
write 6
write 7
write 8
write 9
read 1
read 2
read 3
read 4
read 5
read 6
read 7
read 8
read 9
我再上面每次打印睡眠是为了掩饰效果,如果不睡眠,很快执行完毕,很难看到效果。
锁粒度
在加锁的时候请保持一个原则,最小的锁粒度,在能控制的情况下,尽早的释放锁。
譬如上面的例子
func (p *person) test(act string) {
p.Lock()
defer p.Unlock()
for i:=1;i<10; i++ {
println(act,i)
time.Sleep(time.Millisecond*100)
}
}
这里的defer是对整个函数加锁,如果这里设计到多个操作,都在锁范围,如果这里多个操作,有的并不需要加锁,可以减少,如
func (p *person) test(act string) {
d =curl x.x.x.x
p.Lock()
date[i] = d
p.Unlock()
}
上面的请求就没必要加锁,这个在多个调用的时候可以减少等待时间。
死锁
死锁简单的说就是:你等我,我等你。在golang里面重复的加锁也会导致死锁,看下面例子:
package main
import "sync"
func main() {
println("start")
var m sync.Mutex
m.Lock()
m.Lock()
m.Unlock()
m.Unlock()
println("end")
}
结果如下:
start
fatal error: all goroutines are asleep - deadlock!
死锁了!
最后
以上就是认真鱼为你收集整理的golang学习笔记之并发优化(二)的全部内容,希望文章能够帮你解决golang学习笔记之并发优化(二)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复