互斥锁
同步的用途是避免多个线程在同一时刻操作同一个共享资源。
一个互斥锁只保护一个临界区或一组相关临界区。
一旦声明了 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,不使用互斥锁:
使用互斥锁的注意事项:
- 不用重复锁定互斥锁,会造成死锁;
- 不要忘记解锁互斥锁,必要时使用 defer 语句;
- 不要对尚未锁定或已解锁的互斥锁解锁;
- 不要在多个函数之间直接传递互斥锁。
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
}