go---sync.Mutex 与 sync.RWMutex

互斥锁

同步的用途是避免多个线程在同一时刻操作同一个共享资源。

一个互斥锁只保护一个临界区或一组相关临界区。

一旦声明了 sync.Mutex 类型的变量,就可以直接使用了。

package main

import (
	"fmt"
	"flag"
	"bytes"
	"io"
	"io/ioutil"
	"log"
	"sync"
)

// 0,不使用互斥锁,否则使用
var protecting uint

func init() {
	flag.UintVar(&protecting, "protecting", 1, 
		"It indicates whether to use a mutex to protect data writing.")
}

func main() {
	flag.Parse()
	var buffer bytes.Buffer  // 缓冲区

	const (
		max1 = 5  // 启用 goroutine 的数量
		max2 = 2 // 每个 goroutine 需要写入的数据块的数量
		max3 = 2 // 每个数据块中需要有多少个重复的数字
	)

	var mu sync.Mutex
	sign := make(chan struct{}, max1)

	for i := 1; i <= max1; i++ {
		go func(id int, writer io.Writer) {
			defer func() {
				sign <- struct{}{}
			}()
			
			for j := 1; j <= max2; j++ {
				// 准备数据
				header := fmt.Sprintf("\n[id: %d, iteration: %d]", id, j)
				data := fmt.Sprintf(" %d", id * j)
			
				// 写入数据
				if protecting > 0 {
					mu.Lock()  // 加锁
				}
				_, err := writer.Write([]byte(header))  // 写入头
				if err != nil {
					log.Printf("error: %s [%d]", err, id)
				}
				for k := 0; k < max3; k++ {  // 写入数据
					_, err := writer.Write([]byte(data))
					if err != nil {
						log.Printf("error: %s [%d]", err, id)
					}
				}
			
				if protecting > 0 {
					mu.Unlock()  // 解锁
				}
			}
		}(i, &buffer)
	}

	for i := 0; i < max1; i++ {
		<- sign
	}
	data, err := ioutil.ReadAll(&buffer)
	if err != nil {
		log.Fatalf("fatal error: %s", err)
	}
	log.Printf("The contents:\n%s", data)
}

protecting = 1,使用互斥锁:
在这里插入图片描述
protecting = 0,不使用互斥锁:
在这里插入图片描述
使用互斥锁的注意事项:

  1. 不用重复锁定互斥锁,会造成死锁;
  2. 不要忘记解锁互斥锁,必要时使用 defer 语句;
  3. 不要对尚未锁定或已解锁的互斥锁解锁;
  4. 不要在多个函数之间直接传递互斥锁。
package main

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"log"
	"sync"
	"time"
)

type singleHandler func() (data string, n int, err error)

type handlerConfig struct {
	handler	singleHandler	// 处理函数
	goNum	int		// goroutine 数量
	number	int		// goroutine 中的处理次数
	interval	time.Duration	// goroutine 中的处理间隔
	counter	int		// 数据量计数器,单位为字节
	counterMu sync.Mutex	// 保护 counter 的互斥锁
}

// 增加并返回计数器
func (hc *handlerConfig) count(increment int) int {
	hc.counterMu.Lock()
	defer hc.counterMu.Unlock()
	hc.counter += increment
	return hc.counter
}

func main() {
	var mu sync.Mutex

	// 生成写入函数
	genWriter := func(writer io.Writer) singleHandler {
		return func() (data string, n int, err error) {
			// 准备数据	
			data = fmt.Sprintf("%s\t", time.Now().Format(time.StampNano))
			// 写入数据
			mu.Lock()
			defer mu.Unlock()
			n, err = writer.Write([]byte(data))
			return
		}
	}

	// 生成读取函数
	genReader := func(reader io.Reader) singleHandler {
		return func() (data string, n int, err error) {
			buffer, ok := reader.(*bytes.Buffer)
			if !ok {
				err = errors.New("unsupported reader")
				return
			}
			// 读取数据
			mu.Lock()
			defer mu.Unlock()
			data, err = buffer.ReadString('\t')
			n = len(data)
			return		
		}
	}
	
	var buffer bytes.Buffer

	// 数据写入配置
	writingConfig := handlerConfig {
		handler: genWriter(&buffer),
		goNum: 2,
		number: 3,
		interval: time.Millisecond * 100,
	}

	// 数据读取配置
	readingConfig := handlerConfig {
		handler: genReader(&buffer),
		goNum: 3,
		number: 2,
		interval: time.Millisecond * 100,
	}

	sign := make(chan struct{}, writingConfig.goNum + readingConfig.goNum)

	// 启用多个 goroutine 对缓冲区进行多次数据写入
	for i := 0; i < writingConfig.goNum; i++ {
		go func(i int) {
			defer func() {
				sign <- struct{}{}
			}()

			for j := 0; j < writingConfig.number; j++ {
				time.Sleep(writingConfig.interval)
				data, n, err := writingConfig.handler() // 写入数据
				if err != nil {
					log.Printf("writer [%d-%d]: error: %s", i, j, err)
					continue
				}
				total := writingConfig.count(n)
				log.Printf("writer [%d-%d]: %s (total: %d)", i, j, data, total)
			}
		}(i)
	}

	// 启用多个 goroutine 对缓冲区进行多次读取
	for i := 0;  i < readingConfig.goNum; i++ {
		go func(i int) {
			defer func() {
				sign <- struct{}{}
			}()

			for j := 0; j< readingConfig.number; j++ {
				time.Sleep(readingConfig.interval)
				var data string
				var n int
				var err error
				for {
					data, n, err = readingConfig.handler() // 读取数据
					if err == nil || err != io.EOF{
						break
					}
					// 如果读比写快(读时会发生 EOF 错误),等一会
					time.Sleep(readingConfig.interval)
				}
				if err != nil {
					log.Printf("reader [%d-%d]: error: %s", i, j, data)
					continue
				}
				total := readingConfig.count(n)
				log.Printf("reader [%d-%d]: %s (total: %d)", i, j, data, total)
			}
		}(i)
	}

	signNumber := writingConfig.goNum + readingConfig.goNum
	// 等待上面启用的所有 goroutine 结束
	for j := 0; j < signNumber; j++ {
		<- sign
	}
}

在这里插入图片描述

读写锁

package main

import (
	"log"
	"sync"
	"time"
)

// 计数器
type counter struct {
	num	uint	
	mu	sync.RWMutex
}

// 返回计数
func (c *counter) number() uint {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.num
}

// 增加计数
func (c *counter) add (increment uint) uint {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.num += increment
	return c.num
}

func main() {
	c := counter{}
	sign := make(chan struct{}, 3)

	// 增加计数
	go func() {
		defer func() {
			sign <- struct{}{}
		}()

		for i := 0; i < 3; i++ {
			time.Sleep(time.Millisecond * 500)
			c.add(1)
		}
	}()

	// 读取计数
	go func() {
		defer func() {
			sign <- struct{}{}
		}()

		for i := 0; i< 5; i++ {
			time.Sleep(time.Millisecond * 200)
			n := c.number()
			log.Printf("The number in counter: %d [%d-%d]", n, 0, i)
		}
	}()

	// 读取计数
	go func() {
		defer func() {
			sign <- struct{}{}
		}()

		for i := 0; i< 5; i++ {
			time.Sleep(time.Millisecond * 300)
			n := c.number()
			log.Printf("The number in counter: %d [%d-%d]", n, 1, i)
		}
	}()

	<- sign
	<- sign
	<- sign
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值