Golang sync

chevin · · 2160 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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的实现原理和适用场景》

 

以上。


有疑问加站长微信联系(非本文作者)

本文来自:博客园

感谢作者:chevin

查看原文:Golang sync

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2160 次点击  
加入收藏 微博
上一篇:Golang socket
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传