910 words
5 minutes

Go语言教程:并发与互斥锁

本文将通过一个示例代码,详细讲解Go语言中的并发编程和互斥锁的使用。示例代码展示了如何使用goroutine进行并发操作,以及如何使用互斥锁保护共享资源。

示例代码解析#

导入包#

首先,我们需要导入一些必要的包:

import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"sync"
)
  • bytes:提供了缓冲区操作。
  • flag:用于解析命令行参数。
  • fmt:格式化I/O。
  • io:基本的I/O接口。
  • ioutil:I/O工具函数。
  • log:日志记录。
  • sync:并发原语,包括互斥锁。

全局变量#

var protecting uint

protecting变量用于指示是否使用互斥锁来保护数据写入。值为0表示不使用,值大于0表示使用。

初始化函数#

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

init函数用于初始化命令行参数protecting,默认值为1。

主函数#

func main() {
flag.Parse()
var buffer bytes.Buffer
const (
max1 = 5 // 启用的goroutine数量
max2 = 10 // 每个goroutine写入的数据块数量
max3 = 10 // 每个数据块中重复的数字数量
)
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)
}

代码详解#

  1. 解析命令行参数

    flag.Parse()
  2. 定义缓冲区

    var buffer bytes.Buffer
  3. 常量定义

    const (
    max1 = 5 // 启用的goroutine数量
    max2 = 10 // 每个goroutine写入的数据块数量
    max3 = 10 // 每个数据块中重复的数字数量
    )
  4. 互斥锁和信号通道

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

    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)
    }
  6. 等待所有goroutine完成

    for i := 0; i < max1; i++ {
    <-sign
    }
  7. 读取并打印缓冲区内容

    data, err := ioutil.ReadAll(&buffer)
    if err != nil {
    log.Fatalf("fatal error: %s", err)
    }
    log.Printf("The contents:\\n%s", data)

互斥锁的作用#

在并发编程中,多个goroutine可能会同时访问共享资源,导致数据竞争。互斥锁(sync.Mutex)用于确保同一时间只有一个goroutine可以访问共享资源,从而避免数据竞争。

在本示例中,如果protecting大于0,则会使用互斥锁保护数据写入操作:

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()
}

举一反三#

通过本示例,我们可以举一反三,理解并应用以下概念:

  1. 并发编程:使用goroutine实现并发操作。
  2. 互斥锁:使用sync.Mutex保护共享资源,避免数据竞争。
  3. 信号通道:使用通道(chan)实现goroutine间的同步。

希望通过本教程,您能更好地理解Go语言中的并发编程和互斥锁的使用。祝您编程愉快!

Share

If this article helped you, please share it with others!

Go语言教程:并发与互斥锁
https://blog.ithuo.net/posts/go-concurrency-and-mutex-tutorial/
Author
Derick
Published at
2022-05-23
License
CC BY-NC-SA 4.0
Last updated on 2022-05-23,1327 days ago

Some content may be outdated

Table of Contents