Go 并发实现协程同步的多种解决方法

Go 并发实现协程同步的多种解决方法

在 Go 编程中,对于大量协程的并发执行,我们经常需要对它们进行同步控制,以保证协程之间的正确互动和信息传递。本文介绍 Go 实现协程同步的常用方法,包括使用 WaitGroup、channel、Mutex 等。

使用 WaitGroup

举个例子,我们可能需要同时开启多个协程进行图片下载,且需要等所有协程下载完毕才能继续执行下面的逻辑。这时我们就可以使用 WaitGroup 来控制协程同步。

package main

import (
    "fmt"
    "net/http"
    "sync"
)

func main() {
    urls := []string{
        "https://www.google.com/",
        "https://www.bing.com/",
        "https://www.baidu.com/",
    }

    // 创建 WaitGroup
    var wg sync.WaitGroup

    for _, url := range urls {
        wg.Add(1) // WaitGroup 计数器加 1

        go func(url string) {
            defer wg.Done() // WaitGroup 计数器减 1

            resp, err := http.Get(url)
            if err != nil {
                fmt.Printf("Error: %s\n", err)
                return
            }

            defer resp.Body.Close()
            fmt.Printf("Finished downloading %s\n", url)
        }(url)
    }

    wg.Wait() // 阻塞等待 WaitGroup 计数器为 0
    fmt.Println("All downloads finished")
}

使用 WaitGroup 的主要流程为:

  1. 创建 WaitGroup 对象
  2. 在每个协程启动前,调用 WaitGroup.Add(1) 来将计数器加一
  3. 在协程里面执行任务
  4. 在协程任务结束时,调用 WaitGroup.Done() 来将计数器减一
  5. 最后调用 WaitGroup.Wait() 阻塞等待所有计数器都归零

使用 channel

在 Go 中 channel 也是一种用来同步协程的重要机制。它的主要特点是在协程之间传递数据,并且可以通过阻塞和非阻塞读写操作来实现同步控制。

下面是一个使用 channel 实现协程同步的示例,这里我们模拟一个生产者消费者的场景,其中一个协程作为生产者向 channel 中写数据,另一个协程作为消费者从 channel 中读数据。

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int, 3) // 创建大小为 3 的 channel

    go func() {
        for i := 1; i <= 5; i++ {
            ch <- i
            fmt.Printf("Write %d to channel\n", i)
            time.Sleep(time.Second)
        }
        close(ch) // 关闭 channel
    }()

    go func() {
        for i := range ch {
            fmt.Printf("Read %d from channel\n", i)
            time.Sleep(time.Second)
        }
    }()

    // 阻塞等待所有协程执行完毕
    time.Sleep(5 * time.Second)
    fmt.Println("All goroutines finished")
}

在这个示例中,我们首先创建了一个大小为 3 的 channel。在生产者协程中,我们按顺序向 channel 中写入了 5 个数据。由于 channel 的容量只有 3,当写入了 3 个数据后,协程会阻塞等待消费者协程读取数据。当消费者协程从 channel 中读取 1 个数据后,生产者协程又会继续向 channel 中写入数据,如此往复,直到所有数据都被写入并读取完毕。

其中,range ch 的作用是等待并循环读取 channel 中的数据,直到 channel 被关闭。

使用 Mutex

Mutex 是一种主要用于控制多个协程之间对某个资源进行互斥访问的机制。它的主要作用是保障互斥访问时数据的安全性,避免数据竞争。

下面是使用 Mutex 实现协程同步的示例,其中我们创建一个共享变量 count,同时启动 4 个协程对其进行读写:

package main

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

func main() {
    var wg sync.WaitGroup

    var count int // 共享变量

    var mutex sync.Mutex // 互斥锁

    for i := 0; i < 4; i++ {
        wg.Add(1)

        go func() {
            defer wg.Done()

            // 对 count 进行读
            mutex.Lock()
            fmt.Printf("Read: %d\n", count)
            mutex.Unlock()

            time.Sleep(time.Second)

            // 对 count 进行写
            mutex.Lock()
            count++
            fmt.Printf("Write: %d\n", count)
            mutex.Unlock()
        }()
    }

    wg.Wait()
    fmt.Println("All goroutines finished")
}

在这个示例中,我们首先创建了一个共享变量 count 和一个互斥锁 mutex。在每个协程中,我们先通过 mutex.Lock() 获取互斥锁,然后进行数据访问操作。在对 count 进行写操作完成后,我们释放互斥锁,以此保障其他协程能够顺利地访问并操作同一个共享变量。最后我们调用 wg.Wait() 阻塞式地等待所有协程执行完毕。

以上是 Go 并发实现协程同步的常见方法及其示例。除此之外,Go 还提供了一些类似于 atomic 等原子操作库,以及基于 Context 的超时控制等机制,更加丰富和灵活。在实际应用中,我们需要根据具体的场景和需求来选择合适的同步机制,以保证 Go 程序的正确性和高效性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Go 并发实现协程同步的多种解决方法 - Python技术站

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

相关文章

  • MySQL 数据库如何解决高并发问题

    MySQL 数据库在高并发场景下有多种解决方法,下面我将介绍其中的一些方法。 1. 优化 SQL 语句 首先,要优化 SQL 语句以提高查询速度和降低系统的负载。 1.1 索引 索引是提高查询速度的关键。在创建表时,需要考虑哪些字段需要作为索引来优化查询。不过,索引的过多也会影响写入性能,因此需要根据实际情况来选择适当的索引。 1.2 避免使用 SELECT…

    多线程 2023年5月16日
    00
  • linux并发连接50万的配置方法

    首先,要实现Linux系统并发连接50万的配置,需要考虑以下几个方面: 网络优化 调整TCP的参数,包括window size、backlog、max_tw_buckets等,其中window size模拟并发连接很重要。 增加网卡数量,选择高速网卡,如万兆以太网卡,可以提高网络带宽及IO能力。 使用高效的协议栈,如Google的BBR协议。 资源优化 内核…

    多线程 2023年5月16日
    00
  • 如何在Python中编写并发程序

    一、什么是并发编程 并发编程是指程序同时执行多个任务的一种编程方式。在Python中,这通常通过多线程、多进程和协程来实现。 在多线程、多进程和协程中,每个任务都是独立的,它们可以在不影响其他任务的情况下并发执行,从而提高程序的效率。 二、如何在Python中编写多线程程序 使用threading模块创建线程 Python中内置的threading模块提供了…

    多线程 2023年5月17日
    00
  • 带你快速搞定java多线程(4)

    关于“带你快速搞定Java多线程(4)”这篇文章,下面我来给你详细讲解: 首先,这篇文章主要是讲解Java多线程中的线程池使用,包括线程池的定义、创建、使用和销毁等方面。下面从以下几个方面来介绍。 一. 线程池的定义 线程池是用于存放线程的池子,专门用于管理线程的创建、销毁和复用等工作。在Java程序中,线程池可以通过ThreadPoolExecutor类实…

    多线程 2023年5月17日
    00
  • java多线程中的volatile和synchronized用法分析

    我来详细讲解关于“java多线程中的volatile和synchronized用法分析”的完整攻略。 1. volatile的用法分析 1.1 volatile的概念 volatile是java多线程并发编程中的关键字,可以保证多线程之间可以正确地处理变量的可见性问题,即当一个变量被volatile修饰后,在某个线程中修改该变量值后,修改后的新值立即被写入主…

    多线程 2023年5月17日
    00
  • Java多线程之死锁的出现和解决方法

    Java多线程之死锁的出现和解决方法 死锁的概念 死锁是指在多线程并发的情况下,两个或更多线程在互相等待对方持有的资源,造成程序的无限等待。这种情况下,程序将永远不能终止,只能通过强制终止才能解决。因此,死锁是一种常见的并发编程问题,需要引起我们的重视。 在出现死锁时,我们常用的解决办法是打破死锁的循环依赖关系,从而解除死锁的状态。下面,我们将介绍一些解决死…

    多线程 2023年5月17日
    00
  • 带你快速搞定java多线程(3)

    当我们需要处理一些比较消耗时间的操作时,多线程可以提高程序的执行效率,因此实现多线程在Java编程中也显得尤为重要。本文将带你从多方面快速搞定Java多线程,实现多任务并发执行。 1. 创建线程的三种方式 在Java中,创建线程的方式有三种:继承Thread类、实现Runnable接口以及使用线程池。 1.1 继承Thread类 继承Thread类是最简单的…

    多线程 2023年5月17日
    00
  • python3爬虫中多线程进行解锁操作实例

    下面是详细讲解“python3爬虫中多线程进行解锁操作实例”的完整攻略。 1.什么是多线程 多线程是指同时执行多个线程。多线程能够很好地提高程序的效率,因为在程序执行过程中,如果发生了耗时的操作,比如等待用户输入、文件读写操作、网络数据的传输等,传统的程序会一直等待,造成CPU资源的浪费。而使用多线程可以使得这些操作在后台进行,不会阻塞主线程,提高了程序的响…

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