golang-sync包介绍

一、lock
Mutex互斥锁,也是全局锁;Lock()加锁,Unlock()解锁.

func main() {
	var l *sync.Mutex
	l = new(sync.Mutex)
	l.Lock()
	defer l.Unlock()
	fmt.Println("1")
}


线程安全map
type SafeStringMap struct {
	sync.RWMutex
	M map[string]string
}


var StringMap = &SafeStringMap{M:make(map[string]string)}
func (this *SafeStringMap) Get(key string) (string, bool) {
	this.RLock()
	defer this.RUnlock()
	v, exists := this.M[key]
	return v, exists
}
func (this *SafeStringMap) Set(k string, v string) {
	this.Lock()
	defer this.Unlock()
	this.M[k] = v
}
func (this *SafeStringMap) Get(key string) map[string]string {
	this.RLock()
	defer this.RUnlock()
	return this.M
}


RWMutex读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读次数远远多于写次数的场景.写锁权限高于读锁,有写锁时优先进行写锁定。


基本遵循两大原则:
1、可以随便读,多个goroutine同时读
2、写的时候,啥也不能干。不能读也不能写


RWMutex提供了四个方法:
func (*RWMutex) Lock // 写锁定
func (*RWMutex) Unlock // 写解锁
func (*RWMutex) RLock // 读锁定
func (*RWMutex) RUnlock // 读解锁

二、WaitGroup

WaitGroup等待一组线程集合完成,才会继续向下执行。

func main() {
	var wg sync.WaitGroup
	var urls = []string {
		"http://www.golang.org/",
		"http://www.google.com/",
		"http://www.somestupidname.com/",
	}


	for _, url := range urls {
		wg.Add(1)
		go func(url string) {
			defer wg.Done()
			fmt.Println(url)
		}(url)


	}
	wg.Wait()
	fmt.Println("Game Over")
}



三、sync.Once可以控制函数只能被调用一次。不能多次重复调用
once实现单利模式
import (
"sync"
"fmt"
)


type Singleton map[string]string


var (
	once sync.Once
	instance Singleton
)


func New() Singleton {
	once.Do(func() {
	instance = make(Singleton)


	})
	return instance
	}
	func main() {
	s := New()
	s["test1"] ="aa"
	fmt.Println(s)
	s1 := New() //没有重新初始化
	s1["test2"] = "bb"
	fmt.Println(s1)
}


打印结果:


map[test1:aa]


map[test1:aa test2:bb]






package main
import (
"fmt"
"sync"
"time"
)


 


func main() {
	o := &sync.Once{}
	go do(o)
	go do(o)
	time.Sleep(time.Second * 2)
}
func do(o *sync.Once) {
	fmt.Println("Start do")
	o.Do(func() {
	fmt.Println("Doing something...")
	})
	fmt.Println("Do end")


}


输出结果:
Start do
Doing something...
Do end
Start do
Do end


这里 Doing something 只被调用了一次。




查看go once的源码实现,也是非常的简单:


type Once struct {
	m Mutex
	done uint32
}


func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 1 {
		return
	}
	o.m.Lock()
	defer o.m.Unlock()
	if o.done == 0 {
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}
核心思想是使用原子计数记录被执行的次数。使用Mutex Lock Unlock锁定被执行函数,防止被重复执行。


四、pool 用来保存和复用临时对象,以减少内存分配,降低CG压力。
type Pool
func (p *Pool) Get() interface{}
func (p *Pool) Put(x interface{})
New func() interface{}


Get返回Pool中的任意一个对象。如果Pool为空,则调用New返回一个新创建的对象。如果没有设置New,则返回nil。
func TestPool() {
	var pool sync.Pool
	pool.New = func() interface{} { return "Hello" }
	for i := 0; i < 10; i++ {
	s := pool.Get()
	fmt.Println(s)
	pool.Put("World" + strconv.FormatInt(int64(i), 10))
	}
}


五、条件变量 Cond


等待通知: wait
阻塞当前线程,直到收到该条件变量发来的通知
单发通知: signal
让该条件变量向至少一个正在等待它的通知的线程发送通知,表示共享数据的状态已经改变。
广播通知: broadcast
让条件变量给正在等待它的通知的所有线程都发送通知。
func NewCond(l Locker) *Cond


func TestCond() {
	cond := sync.NewCond(&sync.Mutex{})
	for i := 0; i < 10; i++ {
		go func(t int) {
			time.Sleep(time.Second)
			cond.L.Lock()
			defer cond.L.Unlock()
			cond.Wait()
			fmt.Println(t)
		}(i)
	}
}


time.Sleep(2 * time.Second)


//cond.Signal()


cond.Broadcast()


}




Cond实现生产者、消费者

package main  
  
import (  
    "fmt"  
    "math/rand"  
    "sync"  
    "time"  
)  
  
var cond sync.Cond  
  
//生产者  
func produce(out chan<- int, nu int) {  
    for {  
        cond.L.Lock()  
        //产品区满 等待消费者消费  
        for len(out) == 3 {  
            cond.Wait()  
        }  
        num := rand.Intn(1000)  
        out <- num  
        fmt.Printf("%dth ***producer produce***,num = %d,len(chan) = %d\n", nu, num, len(out))  
        cond.L.Unlock()  
  
        //生产了产品唤醒 消费者线程  
        cond.Signal()  
        //生产完了歇一会,给其他协程机会  
        time.Sleep(time.Second)  
    }  
}  
  
//消费者  
func consume(in <-chan int, nu int) {  
    for {  
        cond.L.Lock()  
        //产品区空 等待生产者生产  
        for len(in) == 0 {  
            cond.Wait()  
        }  
        num := <-in  
        fmt.Printf("%dth ###consumer consume###,num = %d,len(chan) = %d\n", nu, num, len(in))  
        cond.L.Unlock()  
        cond.Signal()  
  
        //消费完了歇一会,给其他协程机会  
        time.Sleep(time.Millisecond * 500)  
    }  
}  
  
func main() {  
    //设置随机数种子  
    rand.Seed(time.Now().UnixNano())  
    quit := make(chan bool)  
    //产品区 使用channel模拟  
    product := make(chan int, 3)  
  
    //创建互斥锁和条件变量  
    cond.L = new(sync.Mutex)  
  
    //5个消费者  
    for i := 0; i < 5; i++ {  
        go produce(product, i)  
    }  
    //3个生产者  
    for i := 0; i < 3; i++ {  
        go consume(product, i)  
    }  
  
    //主协程阻塞 不结束  
    <-quit  
}  





猜你喜欢

转载自blog.csdn.net/gongpulin/article/details/80511599