Go1.9.2 sync库里包含下面几类:Mutex/RWMutex/Cond/WaitGroup/Once/Map/Pool
1.Mutex:互斥锁,等同于linux下的pthread_mutex_t
//多个线程同时运行,获得Mutex锁者线程优先执行,其余线程阻塞等待
func testMutex() {
mutex := sync.Mutex{};
for i := 0; i < 10; i++ {
go func(idx int) {
mutex.Lock();
defer mutex.Unlock();
fmt.Println("idx :=", idx);
time.Sleep(time.Second);
}(i)
}
time.Sleep(20 * time.Second);
fmt.Println("Func finish.");
}
2.RWMutex:读写锁,等同于linux下的pthread_rwlock_t
//写请求在读锁和写锁时都必须阻塞等待,读请求只在写锁时阻塞等待
func testRWMutex() {
rwMutex := sync.RWMutex{};
for i := 0; i < 10; i++ {
go func(idx int) {
rwMutex.RLock();
defer rwMutex.RUnlock();
fmt.Println("Read Mutex :",idx);
}(i);
go func(idx int) {
rwMutex.Lock();
defer rwMutex.Unlock();
fmt.Println("Write Mutex :",idx);
time.Sleep(time.Second);
}(i);
}
time.Sleep(20 * time.Second);
fmt.Println("Func finish.");
}
3.Cond:条件变量,等同于linux下的pthread_cond_t
func testCond() {
cond := sync.NewCond(&sync.Mutex{});
cond.L.Lock(); //①上锁
defer cond.L.Unlock();
go func() {
fmt.Println("go wait lock.");
cond.L.Lock(); //②等Wait解锁
defer cond.L.Unlock(); //⑤解锁后触发Wait
defer fmt.Println("go unlock.");
fmt.Println("go locked.");
cond.Signal(); //④触发Wait等待解锁
}()
time.Sleep(time.Second);
fmt.Println("start wait.");
for {
cond.Wait(); //③可以理解为立刻解锁并触发一个阻塞线程(如果没有阻塞线程则不触发)后立刻再上锁等待Signal信号
fmt.Println("wait finish.");
break;
}
time.Sleep(time.Second);
fmt.Println("Func finish.");
}
4.WaitGroup:组等待
//Add 增加等待计数;Done减少等待计数;当计数为0时触发Wait;
func testWaitGroup() {
waitGroup := sync.WaitGroup{};
for i := 0; i < 10; i++ {
waitGroup.Add(1);
go func(idx int) {
time.Sleep(time.Second);
fmt.Println("go : ", idx);
waitGroup.Done();
}(i)
}
for{
fmt.Println("start wait.");
waitGroup.Wait();
fmt.Println("wait finish.");
break;
}
time.Sleep(time.Second);
fmt.Println("Func finish.");
}
5.Once:只执行一次
//只执行一次以后不再触发
func testOnce() {
once := sync.Once{};
for i := 0; i < 10; i++ {
go func(idx int) {
once.Do(func() {
fmt.Println("Do once : ", idx); //这里只执行一次
})
fmt.Println("go : ", idx);
}(i)
}
time.Sleep(5 * time.Second);
fmt.Println("Func finish.");
}
6.Map:线程安全map
func testMap() {
syncMap := sync.Map{};
for i := 0; i < 10; i++ {
go func(idx int) {
//如果没有则保存起来
_, ok := syncMap.LoadOrStore(idx, " StrVal = "+strconv.FormatInt(int64(idx), 10));
if !ok {
fmt.Println("Store idx = ",idx);
}
}(i)
go func(idx int) {
val, ok := syncMap.Load(idx);
if ok {
fmt.Println("Load success idx = ", idx, val);
} else {
fmt.Println("Load fail idx = ", idx)
}
}(i)
}
time.Sleep(5 * time.Second);
fmt.Println("Func finish.");
}
7.Pool:线程安全对象池
func testPool() {
p := &sync.Pool{
New: func() interface{} {
return -1;
},
}
for i := 0; i < 10; i++ {
go func(idx int) {
p.Put(idx);
}(i)
}
//取出来的对象是无序的
for i := 0; i < 20; i++ {
go func() {
val := p.Get();
fmt.Println("Get val = ", val);
}()
}
time.Sleep(5 * time.Second);
fmt.Println("Func finish.");
}
使用Pool一定要注意一下问题:
1.用途仅仅是增加对象重用的几率,减少gc的负担,而开销方面也不是很便宜的。
2.GC会将Pool清理掉。
3.Get不能保证将Put进去的全部取出来!如下例子:
func testPoolPutGet(){
myPool := &sync.Pool{
New: func() interface{} {
return 0;
},
}
myPool.Put(1) //放入1
myPool.Put(2) //放入2
time.Sleep(time.Second)
p1 := myPool.Get().(int)
fmt.Println(p1) // 获得2
p2 := myPool.Get().(int)
fmt.Println(p2) // 获得0,而不是1!
}
4.关于Pool的实现原理,可以参考《go语言的官方包sync.Pool的实现原理和适用场景》
以上。