GO语言中通道和sync包的使用教程分享

GO语言中通道和sync包的使用教程分享

什么是通道

通道(channel)是 Go 语言中一种特有的同步原语,用于在不同 Goroutine 之间交换数据。通道是一种类型的值,可以对它进行初始化、传递给函数、赋值给变量,甚至可以把它放到切片或结构体中。

创建通道

通道通过 make() 函数来创建,语法如下:

ch := make(chan int)

这里我们创建了一个名为 ch 的通道,元素类型为 int。注意通道只能存储 int 类型的元素。

发送和接收数据

通道可以发数据和收数据,发数据使用“<-”运算符,接收数据则直接使用通道名称。以下是示例代码:

package main

import "fmt"

func main() {
    ch := make(chan int)
    go func() {
        ch <- 2 // 发送数据
    }()
    x := <-ch // 接收数据
    fmt.Print(x)
}

在这个示例中,我们在一个新的 Goroutine 中向通道 ch 中发送了一个整数,然后在主 Goroutine 中接收了这个整数,并打印了它。

若通道中没有数据则会阻塞,如以下示例代码:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)
    go func() {
        time.Sleep(time.Second)
        ch <- 2 // 发送数据
    }()
    x := <-ch // 接收数据
    fmt.Print(x)
}

这个示例中,我们在发送数据前通过 time.Sleep(time.Second) 让程序等待 1 秒钟,程序会先阻塞在这里,等待新的数据被发送。在数据被发送之后,接收方才能获得数据并打印。

sync包

上文讲到的通道只是同步原语的一个应用,Go 标准库还提供了一个专门用于同步的包,叫作 sync

互斥锁

互斥锁是 Go 语言提供的解决并发问题的一种基础同步机制。互斥锁,全称是 Mutual Exclusion Lock,非常类似操作系统中的互斥体和临界区的概念。

在并发编程中,多个并发执行的 Goroutine 会共享资源,若同时对资源进行读写操作,则会导致数据不一致。为了避免这种情况,我们需要使用互斥锁来保护资源。以下是示例代码:

package main

import (
    "fmt"
    "sync"
)

var (
    x  int
    wg sync.WaitGroup
    mu sync.Mutex
)

func main() {
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            mu.Lock()
            x++
            mu.Unlock()
        }()
    }
    wg.Wait()
    fmt.Print(x)
}

在这个示例中,我们开了 100 个 Goroutine,每个 Goroutine 都会将变量 x 加一。由于多个 Goroutine 可能会同时执行 x++,我们需要使用互斥锁 mu.Lock()mu.Unlock() 来保护 x 变量。

读写锁

读写锁是 Go 语言提供的另一种解决并发问题的同步机制,它允许多个 Goroutine 同时读取资源,但只允许一个 Goroutine 写入资源。这种机制在只有少量写入且大量读取的高并发场景下能够显著提升效率。

在使用读写锁时,我们需要先用 sync.RWMutex 创建一个 RWMutex 的实例来进行初始化,并通过 mu.RLock()mu.RUnlock() 来锁定和释放读锁,通过 mu.Lock()mu.Unlock() 来锁定和释放写锁。

以下是一个使用读写锁的示例代码:

package main

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

var (
    x  int
    wg sync.WaitGroup
    mu sync.RWMutex
)

func main() {
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            mu.Lock()
            x++
            mu.Unlock()
        }()
    }
    time.Sleep(time.Second)
    fmt.Printf("x=%d\n", x)

    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            mu.RLock()
            fmt.Printf("x=%d\n", x)
            mu.RUnlock()
        }()
    }
    wg.Wait()
}

在这个示例中,我们同样开了 100 个 Goroutine,前 100 个 Goroutine 会将变量 x 加一,后面的 100 个 Goroutine 则会读取变量 x 的值。我们通过使用读写锁 sync.RWMutex 来保护 x 的读写操作,并通过 mu.RLock()mu.RUnlock() 来锁定和释放读锁,通过 mu.Lock()mu.Unlock() 来锁定和释放写锁。

总结

通过学习本篇文章,我们了解了 Go 语言中通道和 sync 包的基本用法。在多并发的应用场景中,通道和互斥锁/读写锁是非常常用的同步原语。这些机制可以帮助我们在并发执行的 Goroutine 中同步数据,并避免数据的不一致性问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:GO语言中通道和sync包的使用教程分享 - Python技术站

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

相关文章

  • C语言类的基本语法详解

    C语言类的基本语法详解 概述 C语言是一门广泛使用的编程语言,具有较强的系统编程能力。本文将详细介绍C语言的基本语法。 变量 C语言中的变量由其类型和名称两部分组成。变量的类型定义变量能够保存的数据类型,常用的变量类型包括: int: 整数类型,占用4个字节 float: 单精度浮点数类型,占用4个字节 double: 双精度浮点数类型,占用8个字节 cha…

    C 2023年5月22日
    00
  • vbscript,jscript脚本编程教学(1)

    VBScript和JScript脚本编程教学(1) 介绍 VBScript和JScript是微软公司开发的脚本语言,它们的语法和使用方法与JavaScript非常相似。VBScript一般被用于ASP.NET网站的开发,而JScript则一般被用于Windows脚本和Windows PowerShell等环境中。 本教程将重点讲解VBScript和JScri…

    C 2023年5月23日
    00
  • Java日常练习题,每天进步一点点(12)

    Java日常练习题,每天进步一点点(12) – 完整攻略 本题目需要求出给定一组数字中的前k大的数,并进行排序输出。下面是完成此任务的完整攻略: 题目分析 首先,我们需要清楚题目的要求——给定一组数字,求前k大的数并进行排序输出。因此,我们需要以下步骤: 读取输入数字列表; 求出前k大的数字; 将前k大的数字进行排序(从大到小); 输出排序后的前k大数字。 …

    C 2023年5月23日
    00
  • C语言 字符串指针详解及示例代码

    C语言 字符串指针详解及示例代码 什么是字符串指针? 在C语言中,字符串指针通常用来存储字符串的地址,字符串指针变量以及字符串变量有所不同:字符串变量是进行字符串内容及长度操作的,而字符串指针变量不同,它仅存储字符串的地址,这意味着字符串指针变量可以指向不同的字符串。 字符串指针变量的声明方式: char *stringPointer; 字符串指针的赋值 字…

    C 2023年5月24日
    00
  • C++控制台绘图头文件实例代码

    下面是对“C++控制台绘图头文件实例代码”的完整攻略: 1. 简介 在C++的控制台程序中,通过使用图形化绘图头文件,可以在控制台中绘制出各种图形。 2. 下载 在使用绘图头文件前,需要下载对应的库文件。 目前比较流行的库包括: graphics.h:Borland C++ 5.02自带的,不建议使用。 conio.h:Turbo C自带的,也不建议使用。 …

    C 2023年5月24日
    00
  • C语言自定义函数的实现

    C语言中自定义函数的实现可以分为以下几个步骤: 函数声明 : 在使用函数之前,需要先声明函数。函数声明分为两种,一种是函数原型声明,另一种是直接写函数定义。 函数定义:函数定义包括函数名、入参、返回值和函数体。其中函数体是自定义函数的核心部分。 函数调用:调用自定义函数需要使用函数名,并传递相应的参数,等待函数返回相应的结果。 下面,我们用两个示例来说明自定…

    C 2023年5月23日
    00
  • C语言解读数组循环右移问题

    C语言解读数组循环右移问题攻略 1. 问题描述 在数组中,循环右移操作是将数组中的元素向右移动k个位置,其中k为移动的步数,当移动到最后一个元素时,需要将最后一个元素的值作为第一个元素的值。如:[1, 2, 3, 4, 5],循环右移3个位置后变为[3, 4, 5, 1, 2]。 2. 解题思路 循环右移的本质是将原数组分为两部分:左边为需要右移的部分,右边…

    C 2023年5月23日
    00
  • Linux下编译C程序的过程

    下面我会详细讲解如何在Linux系统下编译C程序的完整攻略,流程如下: 步骤一:安装gcc编译器 打开终端,使用以下命令安装gcc编译器: sudo apt-get update sudo apt-get install gcc 安装完成后可以使用以下命令检验是否安装成功: gcc -v 如果出现版本号信息,则表明安装成功。 步骤二:编写C程序 用文本编辑器…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部