GO语言并发编程之互斥锁、读写锁详解

GO语言并发编程之互斥锁、读写锁详解

什么是互斥锁和读写锁

在并发编程中,多个 goroutine(协程)同时访问某个共享资源,容易出现数据竞争的情况,导致程序出现意想不到的结果或全面崩溃。为了解决这个问题,Go 语言提供了互斥锁(Mutex)和读写锁(RWMutex)的机制。

  • 互斥锁:是一个可以被锁定和解锁的标准计数信号量。在同一时刻,只能有一个 goroutine 获取该锁,其他 goroutine 需要等待该锁被释放。通过互斥锁可以实现对临界区(被保护资源)的互斥访问,避免数据竞争。
  • 读写锁:与互斥锁类似,只是它允许多个 goroutine 并发读共享资源。当一个 goroutine 获取了读锁,其他 goroutine 只能再获取读锁,而不能获取写锁。当一个 goroutine 获取了写锁,其他 goroutine 都需要等到写锁被释放后才能获取读锁或写锁。

互斥锁的使用

在Go语言中,通过使用 sync 包中的 Mutex 类型来实现互斥锁功能。Mutex 有两个方法:Lock()Unlock(),能够将访问临界区(被保护资源)的 goroutine 进行同步。

下面是一些互斥锁的使用示例:

package main

import (
    "fmt"
    "sync"
)

type Counter struct {
    val int
    mu  sync.Mutex
}

func (c *Counter) Add() {
    c.mu.Lock()         // 加锁
    defer c.mu.Unlock() // 解锁
    c.val++
}

func (c *Counter) Value() int {
    c.mu.Lock()         // 加锁
    defer c.mu.Unlock() // 解锁
    return c.val
}

func main() {
    c := Counter{}

    var wg sync.WaitGroup
    wg.Add(10000)

    for i := 0; i < 10000; i++ {
        go func(i int) {
            defer wg.Done()
            c.Add()
        }(i)
    }

    wg.Wait()

    fmt.Println(c.Value()) // 10000
}

在上述示例中,创建了一个 Counter 结构体,包含一个整型变量 val 和一个互斥锁 mu。Add() 和 Value() 方法都包含了锁定和解锁的代码。在 main() 函数中,创建了一千个 goroutine 向 Counter 的 val 变量中加一,最终输出的结果是 10000。

通过添加互斥锁,可以完美地解决并发访问资源的问题。

读写锁的使用

读写锁在实现上比互斥锁稍微复杂一些,但是在读多写少的情况下,效率更高。在Go语言中,通过使用 sync 包中的 RWMutex 类型来实现读写锁功能。RWMutex 有四个方法:RLock()RUnlock() 用于管理读锁,Lock()Unlock() 用于管理写锁,能够保证以下操作的安全并发进行:

  • 读操作:多个 goroutine 能够同时获取读锁,读取被保护资源。
  • 写操作:只允许一个 goroutine 获取写锁,防止其他 goroutine 读或写该资源。

下面是一些读写锁的使用示例:

package main

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

type Cache struct {
    value map[string]string
    rw    sync.RWMutex
}

func (c *Cache) Set(key, value string) {
    c.rw.Lock()
    defer c.rw.Unlock()
    c.value[key] = value
}

func (c *Cache) Get(key string) (string, bool) {
    c.rw.RLock()
    defer c.rw.RUnlock()
    value, ok := c.value[key]
    return value, ok
}

func main() {
    cache := Cache{
        value: make(map[string]string),
        rw:    sync.RWMutex{},
    }

    var wg sync.WaitGroup
    wg.Add(2)

    // 写操作
    go func() {
        defer wg.Done()
        for i := 0; i < 5; i++ {
            cache.Set(fmt.Sprintf("key%d", i), fmt.Sprintf("value%d", i))
            time.Sleep(time.Millisecond * 500)
        }
    }()

    // 读操作
    go func() {
        defer wg.Done()
        for i := 0; i < 10; i++ {
            if value, ok := cache.Get(fmt.Sprintf("key%d", i%5)); ok {
                fmt.Printf("key:%s,value:%s\n", fmt.Sprintf("key%d", i%5), value)
            } else {
                fmt.Printf("key:%s,not found\n", fmt.Sprintf("key%d", i%5))
            }
        }
    }()

    wg.Wait()
}

在上述示例中,创建了一个 Cache 结构体,包含一个 map 类型的 value 变量和一个读写锁 rw。通过 Set() 方法往 Cache 的 value 中存入 key/value 数据,通过 Get() 方法从 Cache 的 value 中获取值。

在 main() 函数中,分别创建了两个 goroutine:写 goroutine 往 Cache 中写入五个 key/value,每个 key/value 的间隔为 500ms;读 goroutine 循环获取十个 key,每个 key 的间隔为 100ms。这样,就可以同时对 Cache 中的数据进行读写操作了。

总结

互斥锁和读写锁是Go语言中非常重要的并发编程工具。互斥锁可以实现对共享资源的互斥访问,读写锁可以实现对共享资源的读写分离访问,实现高并发程序的效率和性能优化。在开发过程中,需要注意锁的粒度和时机,避免死锁和性能问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:GO语言并发编程之互斥锁、读写锁详解 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • Linux Shell多进程并发以及并发数控制

    想要实现Linux Shell多进程并发以及并发数控制,可以使用一些经典的工具和技巧。 第一个工具就是xargs,它能够从标准输入中读取参数并将其转换成命令行参数。可以使用-P参数指定一个进程池的大小,从而控制同时运行的进程数。例如: $ find . -name "*.png" | xargs -P 4 -I{} file {} 这个命…

    多线程 2023年5月16日
    00
  • 一篇文章带你入门java多线程

    一篇文章带你入门Java多线程 前言 Java多线程是Java语言的一个非常重要的特性,它可以让我们更好地利用计算机多核的优势,加快程序的运行效率。本文将带你了解Java多线程的基本概念和应用,让你迈出入门的第一步。 Java多线程的基本概念 线程 Java线程是程序中执行的最小单元,一个程序可以有多个线程同时执行。Java线程通过Java.lang.Thr…

    多线程 2023年5月17日
    00
  • golang使用map支持高并发的方法(1000万次操作14ms)

    接下来我会详细讲解怎样使用golang的map实现高并发的方法,并提供两个示例说明。 什么是golang的map golang中的map是一种关联数组(也称为哈希表或字典),它可以用来存储键值对。其中键是唯一的(也称为索引或主键),而值可以是任何类型。对于需要查找、访问和更新键值对的场景,map是非常实用的。 支持高并发的方法 golang中的map默认不支…

    多线程 2023年5月17日
    00
  • 实例讲解Java并发编程之变量

    实例讲解Java并发编程之变量的完整攻略主要分为以下几个部分: 1. 了解共享变量 在Java中,多线程之间经常需要共享变量,这些变量被称为共享变量。由于多个线程同时访问共享变量,因此需要进行同步处理,避免出现数据不一致的情况。Java提供了多种同步机制,例如synchronized、volatile、Lock等。 2. 使用volatile关键字 vola…

    多线程 2023年5月16日
    00
  • java多线程编程之从线程返回数据的两种方法

    首先让我们来了解几个基本的概念: 线程(Thread):计算机中最小的执行单元之一,负责执行程序中指定的任务。 多线程(Multithreading):指在同一个程序中同时执行多个线程,避免单一线程运行太慢造成CPU的浪费。 线程返回数据(Thread Return Data):线程计算完成后,将得到的结果返回给主线程,主线程可以做出相应的操作。 为了实现线…

    多线程 2023年5月16日
    00
  • 详解进程同步与互斥机制

    详解进程同步与互斥机制 什么是进程同步和互斥? 在多进程环境下,多个进程之间共享计算机资源,例如共享内存区域。有时多个进程需要访问同一资源,这时候需要协调它们之间的访问,以免数据出现混乱。 进程同步是指协调多个进程之间的活动以达到一致的状态。进程互斥是指规范多个进程在不同时间访问资源的竞争环境,以防止它们同时访问同一资源而导致不可预测的后果。 进程同步的方法…

    多线程 2023年5月17日
    00
  • Mysql事务并发脏读+不可重复读+幻读详解

    Mysql事务并发脏读+不可重复读+幻读详解 事务 数据库事务是指一系列的数据库操作,它们作为一个单独的工作单元执行,要么全部执行成功,要么全部执行失败,保证数据的一致性和可靠性,是数据库的一项非常重要的功能。 在Mysql中,如果想要执行一系列的操作能够作为一个事务,需要使用在InnoDB引擎下提供的BEGIN、COMMIT、ROLLBACK等SQL命令进…

    多线程 2023年5月17日
    00
  • JAVA线程用法详解

    JAVA线程用法详解 线程基础知识 线程定义 线程可以理解为轻量级的进程,是程序执行的一条单独的路径。一个程序中通常可以有多个线程同时执行不同的任务,线程之间可以共享程序的数据和资源,因此其效率比多进程更高。 JAVA中,线程是Thread类的实例,在程序中启动和控制线程的执行需要调用Thread类中的方法。 线程状态 线程的状态可以分为以下5种: 新建状态…

    多线程 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部