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日

相关文章

  • MySQL并发更新数据时的处理方法

    MySQL并发更新数据时的处理方法 在MySQL中,当多个用户同时对同一行数据进行修改时,会发生并发更新的情况。这会带来脏读、丢失更新等问题,影响数据的完整性。因此,需要采取一些方法来处理并发更新。 1. 悲观锁 悲观锁是指在操作数据时,认为其他用户会同时访问该数据,因此在操作数据之前,先对其进行加锁,防止其他用户修改该数据。在MySQL中,可以使用SELE…

    多线程 2023年5月16日
    00
  • 详解Java多线程和IO流的应用

    详解Java多线程和IO流的应用 简介 Java多线程和IO流是Java编程中非常重要的两个主题。多线程可以帮助我们充分利用计算机多核处理器的性能,从而提高程序运行效率。而IO流则可以帮助我们进行文件读写、网络通信等操作。本文将从基础概念讲解和实际例子两个方面介绍Java多线程和IO流的应用。 基础概念讲解 多线程 Java多线程是指在同一时刻,多条线程同时…

    多线程 2023年5月17日
    00
  • springboot内置tomcat调优并发线程数解析

    下面是对“springboot内置tomcat调优并发线程数解析”的完整攻略,包含以下几个部分: 概述 Springboot作为一个常用的Java Web应用框架,内置了Tomcat作为默认的Web容器。在高并发的场景下,调整Tomcat的并发线程数是必不可少的,可以提高系统的稳定性和性能。 调优 Tomcat的并发线程数可以通过“server.tomcat…

    多线程 2023年5月17日
    00
  • linux多线程编程详解教程(线程通过信号量实现通信代码)

    Linux多线程编程是现代操作系统最基本、也是最重要的部分之一。在实际应用开发中,多线程编程可以优化程序的性能,提高代码运行效率。本文将详细介绍如何通过信号量实现线程之间的通信,包含完整的代码示例。 一、什么是信号量? 信号量是一种用于多线程同步互斥的机制,用来协调进程对共享资源的访问。信号量是一个计数器,用来记录一个共享资源的数量,当某个进程需要使用该资源…

    多线程 2023年5月17日
    00
  • Python使用asyncio包处理并发详解

    当今网络服务越来越注重并发访问的处理,常见的异步框架有 gevent, twisted, tornado等,而作为一个优秀的异步框架,Python的asyncio更是备受关注。Asyncio 是 Python 3.4 新增的异步IO模块,它提供了基于协程的异步编程方式,使得异步编程更加易用、高效、可控。 下面我们来详细介绍Python中使用asyncio包进…

    多线程 2023年5月17日
    00
  • java虚拟机中多线程总结

    Java虚拟机中多线程总结 Java是一种支持多线程的编程语言,可以在同一个程序中同时运行多个线程。Java虚拟机(JVM)是Java程序的核心组件之一,多线程是JVM提供的一项非常重要的功能。在JVM中,多线程的实现方式主要有两种:基于进程的多线程和基于原生线程的多线程。 基于进程的多线程 基于进程的多线程是指在JVM内部使用单独的进程来实现多线程。这种多…

    多线程 2023年5月17日
    00
  • 15个高级Java多线程面试题及回答

    15个高级Java多线程面试题及回答 本文将详细介绍 15 个高级 Java 多线程面试题及回答,以下是题目列表: 在 Java 中,什么是线程死锁,如何避免死锁? 什么是线程池,在多线程编程中,为什么要使用线程池? 请解释 synchronized 和 volatile 关键字的用途。 从编程的角度来看,什么是竞态条件? 如何在 Java 中实现可重入锁?…

    多线程 2023年5月16日
    00
  • java高并发ScheduledThreadPoolExecutor与Timer区别

    Java高并发ScheduledThreadPoolExecutor与Timer区别攻略 在开发过程中,我们经常需要实现定时任务,此时Java提供了两种处理定时任务的类:ScheduledThreadPoolExecutor和Timer。这两个类都可以完成定时任务的功能,本文将分别介绍它们的区别和使用场景。 ScheduledThreadPoolExecut…

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