Golang并发控制-内存访问原语(一)

WaitGroup

结构与特点

type WaitGroup struct {
	//隐藏字段或非导出字段
}

特点: 用于等待一组goroutine的结束,并且不关心并发操作的结果,或者有其他方式得到并发操作的结果。

方法

func (wg *WaitGroup) Add(delta int)   //内部计数增加delta,delta可为负数。
                                      //如果内部计数器小于0,方法会panic。
                                      //一般在创建goroutine前执行,以保证在main goroutine调用Wait方法前计数器增加
                                      
func (wg *WaitGroup) Done()           //将内部计数减1, 一般在goroutine最后执行,使用defer关键字
									  //应保证在Add方法之后调用
									  
func (wg *WaitGroup) Wait()           //Wait方法阻塞,直到内部计数器值为0

示例1

package main

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

func main() {
	var wg sync.WaitGroup

	wg.Add(1)
	go func() {
		defer wg.Done()
		fmt.Println("1st goroutine sleeping...")
		time.Sleep(1)
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		fmt.Println("2st goroutine sleeping...")
		time.Sleep(2)
	}()

	wg.Wait()
	fmt.Println("All goroutines complete.")
}

示例2

package main

import (
	"fmt"
	"sync"
)

func hello(wg *sync.WaitGroup, id int) {
	defer wg.Done()
	fmt.Printf("Hello from %v!\n", id)
}

func main() {
	var wg sync.WaitGroup
	const numGreeters = 5
	wg.Add(numGreeters)
	for i := 0; i < numGreeters; i++ {
		go hello(&wg, i+1)
	}
	wg.Wait()
}

Mutex与RWMutex

结构与特点

type Mutex struct {
	//隐藏字段或非导出字段
}
type RWMutex struct {
	//隐藏字段或非导出字段
}

特点: 互斥锁与读写锁,用于访问临界区时的同步控制。

方法

func (m *Mutex) Lock()       //互斥锁加锁
func (m *Mutex) Unlock()     //互斥锁解锁,对未锁定的锁解锁会导致运行时错误
func (rw *RWMutex) Lock()    //读写锁加写锁
func (rw *RWMutex) Unlock()  //读写锁解写锁,对未锁定的锁解锁会导致运行时错误
func (rw *RWMutex) RLock()   //读写锁加读锁
func (rw *RWMutex) RUnlock() //读写锁解读锁,对未锁定的锁解锁会导致运行时错误

示例1

package main

import (
	"fmt"
	"sync"
)

func main() {
	var count int
	var lock sync.Mutex

	increment := func() {
		lock.Lock()         //critical section加锁,这部分是性能的瓶颈
		defer lock.Unlock() //保证panic时此操作也会执行,否则程序会死锁
		count++
		fmt.Printf("Incrementing: %d\n", count)
	}

	decrement := func() {
		lock.Lock()
		defer lock.Unlock()
		count--
		fmt.Printf("Decrementing: %d\n", count)
	}

	var arithmetic sync.WaitGroup
	for i := 0; i < 5; i++ {
		arithmetic.Add(1)
		go func() {
			defer arithmetic.Done()
			increment()
		}()
	}

	for i := 0; i < 5; i++ {
		arithmetic.Add(1)
		go func() {
			defer arithmetic.Done()
			decrement()
		}()
	}
	arithmetic.Wait()
	fmt.Println("Arithmetic complete.")
}

示例2

package main

import (
	"fmt"
	"math"
	"os"
	"sync"
	"text/tabwriter"
	"time"
)

func main() {
	producer := func(wg *sync.WaitGroup, l sync.Locker) {
		defer wg.Done()
		for i := 5; i > 0; i-- {
			l.Lock()
			l.Unlock()
			time.Sleep(1)
		}
	}

	observer := func(wg *sync.WaitGroup, l sync.Locker) {
		defer wg.Done()
		l.Lock()
		defer l.Unlock()
	}

	test := func(count int, mutex, rwMutex sync.Locker) time.Duration {
		var wg sync.WaitGroup
		wg.Add(count + 1)
		beginTestTime := time.Now()
		go producer(&wg, mutex)                  //获取写锁, 5轮
		for i := count; i > 0; i-- {             //获取读锁, count轮, 下文中比较了加读锁与加写锁的时间
			go observer(&wg, rwMutex)
		}

		wg.Wait()
		return time.Since(beginTestTime)
	}

	tw := tabwriter.NewWriter(os.Stdout, 0, 1, 2, ' ', 0)   //输出格式参数
	defer tw.Flush() //刷新输出

	var m sync.RWMutex
	fmt.Fprintf(tw, "Readers\tRWMutex\tMutex\n")
	for i := 0; i < 18; i++ {
		count := int(math.Pow(2, float64(i)))
		fmt.Fprintf(
			tw,
			"%d\t%v\t%v\n",
			count,
			test(count, &m, m.RLocker()), //RWMutex的RLocker方法返回一个以RLock和RUnlock实现Lock接口的对象
			test(count, &m, &m),
		)
	}
}

Once

结构与特点

type Once struct {
    // 包含隐藏或非导出字段
}

特点:通过底层原语控制函数只执行一次。

方法

func (o *Once) Do(f func())   //Do方法当且仅当第一次调用时才会执行函数f,即使多次调用的参数f不同

示例1

package main

import (
	"fmt"
	"sync"
)

func main() {
	var count int
	increment := func() { count++ }
	decrement := func() { count-- }

	var once sync.Once
	once.Do(increment)
	once.Do(decrement)

	fmt.Printf("Count: %d\n", count) //输出 Count: 1
}

示例2

package main

import "sync"

func main() {
	var onceA, onceB sync.Once
	var initB func()
	initA := func() { onceB.Do(initB) }
	initB = func() { onceA.Do(initA) }
	onceA.Do(initA)
	//此程序会导致死锁,然而不是因为Once内部问题而是逻辑问题导致的循环引用
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值