Go语言学习之sync包(临时对象池Pool、互斥锁Mutex、等待Cond)(the way to go)

golang的特点就是语言层面支持并发,并且实现并发非常简单,只需在需要并发的函数前面添加关键字go
但是如何处理go并发机制中不同goroutine之间的同步与通信,golang 中提供了sync包来解决相关的问题,当然还有其他的方式比如channel,原子操作atomic等等,这里先介绍sync包的用法.

这里,跟大家一些学习golang的标准库,sync。

package sync

简介

Package sync provides basic synchronization primitives such as mutual exclusion locks. Other than the Once and WaitGroup types, most are intended for use by low-level library routines. Higher-level synchronization is better done via channels and communication.

sync 包提供了互斥锁这类的基本的同步原语.除 Once 和 WaitGroup 之外的类型大多用于底层库的例程。更高级的同步操作通过信道与通信进行。

Cond

Cond用于在并发环境下routine的等待和通知
type Cond

type Cond struct {

        // L is held while observing or changing the condition
        L Locker
        // contains filtered or unexported fields
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在“检查条件”或“更改条件”时 L 应该锁定

func NewCond

func NewCond(l Locker) *Cond
  
  
  • 1

NewCond returns a new Cond with Locker l.
创建一个条件等待

func (*Cond) Broadcast

func (c *Cond) Broadcast()
  
  
  • 1

Broadcast wakes all goroutines waiting on c.

It is allowed but not required for the caller to hold c.L during the call.
Broadcast 唤醒所有等待的 Wait,建议在“更改条件”时锁定 c.L,更改完毕再解锁

func (*Cond) Signal

func (c *Cond) Signal()
  
  
  • 1

Signal wakes one goroutine waiting on c, if there is any.

It is allowed but not required for the caller to hold c.L during the call.
Signal 唤醒一个等待的 Wait,建议在“更改条件”时锁定 c.L,更改完毕再解锁。

func (*Cond) Wait

func (c *Cond) Wait()
  
  
  • 1

Wait atomically unlocks c.L and suspends execution of the calling goroutine. After later resuming execution, Wait locks c.L before returning. Unlike in other systems, Wait cannot return unless awoken by Broadcast or Signal.

Because c.L is not locked when Wait first resumes, the caller typically cannot assume that the condition is true when Wait returns. Instead, the caller should Wait in a loop:
Wait 会解锁 c.L 并进入等待状态,在被唤醒时,会重新锁定 c.L

使用示例:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    m := sync.Mutex{}
    m.Lock()
    c := sync.NewCond(&m)
    go func() {
        m.Lock()
        defer m.Unlock()
        fmt.Println("3. goroutine is owner of lock")
        time.Sleep(2 * time.Second)
        c.Broadcast() //唤醒所有等待的 Wait
        fmt.Println("4. goroutine will release lock soon (deffered Unlock)")
    }()
    fmt.Println("1. main goroutine is owner of lock")
    time.Sleep(1 * time.Second)
    fmt.Println("2. main goroutine is still lockek")
    c.Wait()
    m.Unlock()
    fmt.Println("Done")
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

输出:
1. main goroutine is owner of lock
2. main goroutine is still lockek
3. goroutine is owner of lock
4. goroutine will release lock soon (deffered Unlock)
Done

Map

并发安全的map,关于golang中map的并发不安全之后再详细介绍。
type Map

type Map struct {
        // contains filtered or unexported fields
}
  
  
  • 1
  • 2
  • 3

func (*Map) Delete

func (m *Map) Delete(key interface{})
  
  
  • 1

Delete deletes the value for a key.
删除一个键值。

func (*Map) Load

func (m *Map) Load(key interface{}) (value interface{}, ok bool)
  
  
  • 1

Load returns the value stored in the map for a key, or nil if no value is present. The ok result indicates whether value was found in the map.
加载方法,也就是提供一个键key,查找对应的值value,如果不存在,通过ok反映。

func (*Map) LoadOrStore

func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
  
  
  • 1

LoadOrStore returns the existing value for the key if present. Otherwise, it stores and returns the given value. The loaded result is true if the value was loaded, false if stored.

func (*Map) Range

func (m *Map) Range(f func(key, value interface{}) bool)
  
  
  • 1

Range calls f sequentially for each key and value present in the map. If f returns false, range stops the iteration.
for … range map是内建的语言特性,所以没有办法使用for range遍历sync.Map, 但是可以使用它的Range方法,通过回调的方式遍历。

func (*Map) Store

func (m *Map) Store(key, value interface{})
  
  
  • 1

Store sets the value for a key.
更新或者新增一个entry

使用示例:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var m sync.Map
    for i := 0; i < 3; i++ {
        go func(i int) {
            for j := 0; ; j++ {
                m.Store(i, j)
            }
        }(i)
    }
    for i := 0; i < 10; i++ {
        m.Range(func(key, value interface{}) bool {
            fmt.Printf("%d: %d\t", key, value)
            return true
        })
        fmt.Println()
        time.Sleep(time.Second)
    }
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

输出:
无,要更新go1.9

Locker Mutex

互斥锁用来保证在任一时刻,只能有一个例程访问某对象。Mutex 的初始值为解锁状态。Mutex 通常作为其它结构体的匿名字段使用,使该结构体具有 Lock 和 Unlock 方法。

type Locker

type Locker interface {
        Lock()
        Unlock()
}
  
  
  • 1
  • 2
  • 3
  • 4

A Locker represents an object that can be locked and unlocked.
Locker 接口包装了基本的 Lock 和 UnLock 方法,用于加锁和解锁。

type Mutex

type Mutex struct {
        // contains filtered or unexported fields
}
  
  
  • 1
  • 2
  • 3

func (*Mutex) Lock

func (m *Mutex) Lock()
  
  
  • 1

Lock locks m. If the lock is already in use, the calling goroutine blocks until the mutex is available.
Lock 用于锁住 m,如果 m 已经被加锁,则 Lock 将被阻塞,直到 m 被解锁。

func (*Mutex) Unlock

func (m *Mutex) Unlock()
  
  
  • 1

Unlock unlocks m. It is a run-time error if m is not locked on entry to Unlock.
Unlock 用于解锁 m,如果 m 未加锁,则该操作会引发 panic。

使用示例:

package main

import (
    "fmt"
    "sync"
)

type SafeInt struct {
    sync.Mutex
    Num int
}

func main() {
    count := SafeInt{}
    done := make(chan bool)
    for i := 0; i < 10; i++ {
        go func(i int) {
            count.Lock()
            count.Num += i
            fmt.Print(count.Num, " ")
            count.Unlock()
            done <- true
        }(i)
    }
    for i := 0; i < 10; i++ {
        <-done
    }
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

输出(随机):
0 9 10 12 15 19 24 30 37 45

Once

Once is an object that will perform exactly one action.

Once 的作用是多次调用但只执行一次,Once 只有一个方法,Once.Do(),向 Do 传入一个函数,这个函数在第一次执行 Once.Do() 的时候会被调用,以后再执行 Once.Do() 将没有任何动作,即使传入了其它的函数,也不会被执行,如果要执行其它函数,需要重新创建一个 Once 对象。

type Once

type Once struct {
        // contains filtered or unexported fields
}
  
  
  • 1
  • 2
  • 3

func (*Once) Do

func (o *Once) Do(f func())
  
  
  • 1

Do calls the function f if and only if Do is being called for the first time for this instance of Once. In other words, given

使用示例:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {

    o := &sync.Once{}
    go myfun(o)
    go myfun(o)
    time.Sleep(time.Second * 2)
}

func myfun(o *sync.Once) {

    fmt.Println("Begin function")

    o.Do(func() {
        fmt.Println("Working...")
    })

    fmt.Println("Function end")
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

输出:
Begin function
Working…
Function end
Begin function
Function end

Pool

Pool 用于存储临时对象,它将使用完毕的对象存入对象池中,在需要的时候取出来重复使用,目的是为了避免重复创建相同的对象造成 GC 负担过重。其中存放的临时对象随时可能被 GC 回收掉(如果该对象不再被其它变量引用)。

从 Pool 中取出对象时,如果 Pool 中没有对象,将返回 nil,但是如果给 Pool.New 字段指定了一个函数的话,Pool 将使用该函数创建一个新对象返回。

Pool 可以安全的在多个例程中并行使用,但 Pool 并不适用于所有空闲对象,Pool 应该用来管理并发的例程共享的临时对象,而不应该管理短寿命对象中的临时对象,因为这种情况下内存不能很好的分配,这些短寿命对象应该自己实现空闲列表。

切记,Pool 在开始使用之后,不能再被复制!!!!

type Pool

type Pool struct {

        // New optionally specifies a function to generate
        // a value when Get would otherwise return nil.
        // It may not be changed concurrently with calls to Get.
        New func() interface{}
        // contains filtered or unexported fields
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

func (*Pool) Get

func (p *Pool) Get() interface{}
  
  
  • 1

作用:从临时对象池中取出对象

func (*Pool) Put

func (p *Pool) Put(x interface{})
  
  
  • 1

作用:向临时对象池中存入对象

使用示例:

package main

import (
    "fmt"
    "sync"
)

func main() {
    p := &sync.Pool{
        New: func() interface{} {
            return 0
        },
    }

    a := p.Get().(int)
    p.Put(100)
    b := p.Get().(int)
    fmt.Println(a, b)
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

输出:0 100

RWMutex

RWMutex 比 Mutex 多了一个“读锁定”和“读写解锁”,可以让多个例程同时读取某对象。

RWMutex 可以安全的在多个例程中并行使用。

type RWMutex

type RWMutex struct {
        // contains filtered or unexported fields
}
  
  
  • 1
  • 2
  • 3

func (*RWMutex) Lock

func (rw *RWMutex) Lock()
  
  
  • 1

Lock locks rw for writing. If the lock is already locked for reading or writing, Lock blocks until the lock is available.
Lock 将 rw 设置为写锁定状态,禁止其他例程读取或写入

func (*RWMutex) RLock

func (rw *RWMutex) RLock()
  
  
  • 1

RLock locks rw for reading.
RLock 将 rw 设置为读锁定状态,禁止其他例程写入,但可以读取。
It should not be used for recursive read locking; a blocked Lock call excludes new readers from acquiring the lock. See the documentation on the RWMutex type.

func (*RWMutex) RLocker

func (rw *RWMutex) RLocker() Locker
  
  
  • 1

RLocker returns a Locker interface that implements the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.

func (*RWMutex) RUnlock

func (rw *RWMutex) RUnlock()
  
  
  • 1

RUnlock undoes a single RLock call; it does not affect other simultaneous readers. It is a run-time error if rw is not locked for reading on entry to RUnlock.
Runlock 解除 rw 的读锁定状态,如果 rw 未被读锁顶,则该操作会引发 panic。

func (*RWMutex) Unlock

func (rw *RWMutex) Unlock()
  
  
  • 1

Unlock unlocks rw for writing. It is a run-time error if rw is not locked for writing on entry to Unlock.
Unlock 解除 rw 的写锁定状态,如果 rw 未被写锁定,则该操作会引发 panic。

使用示例:

package main

import (
    "sync"
    "time"
)

var m *sync.RWMutex

func main() {
    m = new(sync.RWMutex)

    go read(1)
    go read(2)

    time.Sleep(2 * time.Second)
}

func read(i int) {
    println(i, "start")

    m.RLock()
    println(i, "reading......")
    time.Sleep(1 * time.Second)
    m.RUnlock()

    println(i, "end")
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

输出:
1 start
1 reading……
2 start
2 reading……
1 end
2 end

WaitGroup

WaitGroup 用于等待一组例程的结束。主例程在创建每个子例程的时候先调用 Add 增加等待计数,每个子例程在结束时调用 Done 减少例程计数。之后,主例程通过 Wait 方法开始等待,直到计数器归零才继续执行。

type WaitGroup

type WaitGroup struct {
        // contains filtered or unexported fields
}
  
  
  • 1
  • 2
  • 3

func (*WaitGroup) Add

func (wg *WaitGroup) Add(delta int)
  
  
  • 1

Add adds delta, which may be negative, to the WaitGroup counter. If the counter becomes zero, all goroutines blocked on Wait are released. If the counter goes negative, Add panics.
计数器增加 delta,delta 可以是负数。

func (*WaitGroup) Done

func (wg *WaitGroup) Done()
  
  
  • 1

Done decrements the WaitGroup counter by one.
计数器减少 1

func (*WaitGroup) Wait

func (wg *WaitGroup) Wait()
  
  
  • 1

Wait blocks until the WaitGroup counter is zero.
等待直到计数器归零。如果计数器小于 0,则该操作会引发 panic。

使用示例:

package main

import (
    "fmt"
    "sync"
    "time"
)

func dosomething(millisecs time.Duration, wg *sync.WaitGroup) {
    duration := millisecs * time.Millisecond
    time.Sleep(duration)
    fmt.Println("Function in background, duration:", duration)
    wg.Done()
}

func main() {
    var wg sync.WaitGroup
    wg.Add(4)
    go dosomething(200, &wg)
    go dosomething(400, &wg)
    go dosomething(150, &wg)
    go dosomething(600, &wg)

    wg.Wait()
    fmt.Println("Done")
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

输出:
Function in background, duration: 150ms
Function in background, duration: 200ms
Function in background, duration: 400ms
Function in background, duration: 600ms
Done

简单应用

通过sync实现单例

package singleton

import (
    "sync"
)

type singleton struct {
}

var instance *singleton
var once sync.Once

func GetInstance() *singleton {
    once.Do(func() {
        instance = &singleton{}
    })
    return instance
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

访问多个url

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "sync"
)

func main() {
    urls := []string{
        "http://blog.csdn.net/wangshubo1989/article/details/77949252",
        "http://blog.csdn.net/wangshubo1989/article/details/77933654",
        "http://blog.csdn.net/wangshubo1989/article/details/77893561",
    }
    jsonResponses := make(chan string)

    var wg sync.WaitGroup

    wg.Add(len(urls))

    for _, url := range urls {
        go func(url string) {
            defer wg.Done()
            res, err := http.Get(url)
            if err != nil {
                log.Fatal(err)
            } else {
                defer res.Body.Close()
                body, err := ioutil.ReadAll(res.Body)
                if err != nil {
                    log.Fatal(err)
                } else {
                    jsonResponses <- string(body)
                }
            }
        }(url)
    }

    go func() {
        for response := range jsonResponses {
            fmt.Println(response)
        }
    }()

    wg.Wait()
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

这里写图片描述

猜你喜欢

转载自blog.csdn.net/double_happiness/article/details/84756140
way
go