微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

golang学习笔记之并发优化二

打包发送

通往管道的数据如果一次打包发送的性能要高于多次发送。请看下面里面,这个里面我往管道里面发送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,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! 死锁了!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐