S1E1:GO中的并发| Goroutine |频道| waitgroup
#go #并发性 #designpatterns #systemdesign

我很高兴地宣布,我已经开始使用工作代码的有关并发设计模式的新系列。该播放列表将包含9-10个编码 +概念视频。有关完整的详细说明,请转到 codepiper youtube频道视频:https://youtu.be/eTJ7P0RCZJc


此博客将涵盖

  1. goroutines
  2. Waitgroup
  3. 频道
  4. 缓冲通道
  5. 并发僵局

goroutine:
Goroutines是GO中并发编程的基础(通常称为Golang)。它们是轻巧的,独立执行的功能,与同一地址空间中的其他goroutines同时运行。 Goroutines由GO运行时管理,他们通过一种称为“多路复用”的技术有效地利用了可用的CPU内核。您可以将Goroutine视为独立和同时运行其他功能的函数,从而可以在GO程序中有效且可扩展的并发。

func printNumbers() {
    for i := 1; i <= 5; i++ {
        fmt.Printf("%d ", i)
        time.Sleep(100 * time.Millisecond)
    }
}

func printLetters() {
    for ch := 'a'; ch <= 'e'; ch++ {
        fmt.Printf("%c ", ch)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    go printNumbers() // Start a new Goroutine for printNumbers()
    printLetters()    // Execute printLetters() concurrently with the Goroutine
}

waitgroup:
WaitGroup是同步软件包中GO标准库提供的同步原始。它用于等待一系列Goroutines来完成执行,然后再在主要Goroutine中继续进行。当Goroutine完成任务时,候补组本质上是一个计数器,当启动并减少新的Goroutine时,它会增加。主goroutine可以在WaitGroup上调用WAIT,该Group将阻塞直到计数器变为零(即,所有Goroutines都完成了其任务并在WaitGroup上完成了任务)。它有助于协调多个goroutines的终止。

func printNumbers(wg *sync.WaitGroup) {
    defer wg.Done() // Notify WaitGroup that this Goroutine is done when the function returns
    for i := 1; i <= 5; i++ {
        fmt.Printf("%d ", i)
        time.Sleep(100 * time.Millisecond)
    }
}

func printLetters(wg *sync.WaitGroup) {
    defer wg.Done() // Notify WaitGroup that this Goroutine is done when the function returns
    for ch := 'a'; ch <= 'e'; ch++ {
        fmt.Printf("%c ", ch)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    var wg sync.WaitGroup
    wg.Add(2) // Add the number of Goroutines to wait for

    go printNumbers(&wg) // Start a new Goroutine for printNumbers()
    go printLetters(&wg) // Start a new Goroutine for printLetters()

    wg.Wait() // Wait until all Goroutines in the WaitGroup finish
    fmt.Print("Okay you can go")
}

频道:
GO中的通道是一个打字导管,可以在不同的goroutines之间进行通信和同步。这是Goroutines向彼此发送和接收值的一种方式。渠道提供了一种安全的方式来交换数据并在Goroutines之间进行交流,防止种族条件和其他与并发有关的问题。通道可用于将数据从一个goroutine传递到另一个goroutine,从而有效地协调了并发任务。频道可以根据其容量而被禁止或缓冲。

func sender(ch chan<- int) {
    for i := 1; i <= 10; i++ {
        ch <- i // Send data to the channel
    }
    close(ch) // Close the channel after sending all data
}

func receiver(ch <-chan int) {
    for num := range ch {
        fmt.Printf("%d ", num) // Receive data from the channel
    }
}

func main() {
    ch := make(chan int)

    go sender(ch) // Start a new Goroutine for the sender
    receiver(ch)  // Execute the receiver concurrently with the Goroutine
}

缓冲通道:
缓冲通道是GO中的一种类型的通道,具有固定能力容纳一定数量的元素。创建缓冲通道时,您将容量指定为制造功能的第二个参数。例如,ch:= make(chan int,10)。在这种情况下,CHUNCH CH最多可容纳10个元素。当Goroutine将值发送到缓冲通道时,如果有空间,它将被添加到通道的内部缓冲区中。如果通道已满,则发送Goroutine将阻塞直到缓冲区中有空间。同样,当Goroutine试图从缓冲通道接收值时,如果不是空的,它将从缓冲区接收数据。如果缓冲区为空,则接收goroutine将阻止直到数据可用。

func main() {
    ch := make(chan int, 3) // Create a buffered channel with a capacity of 3

    ch <- 1 // Send data to the channel
    ch <- 2 // Send more data
    ch <- 3 // Send even more data

    // ch <- 4 // Sending a 4th value would cause a deadlock because the channel is full

    fmt.Println(<-ch) // Receive data from the channel
    fmt.Println(<-ch) // Receive more data
    fmt.Println(<-ch) // Receive even more data

    // fmt.Println(<-ch) // Receiving a 4th value would cause a deadlock because the channel is empty
}

注意:使用缓冲通道有时可以通过降低goroutines之间的争论来改善并发程序的性能,只要根据特定用例明智地选择缓冲区大小。但是,要注意缓冲尺寸以避免消耗过多的记忆。

func producer(ch chan<- int) {
    for i := 1; i <= 5; i++ {
        fmt.Printf("Producing %d\n", i)
        ch <- i
        time.Sleep(500 * time.Millisecond) // Simulate some work by the producer
    }
    close(ch)
}

func consumer(ch <-chan int) {
    for num := range ch {
        fmt.Printf("Consuming %d\n", num)
        time.Sleep(1 * time.Second) // Simulate some work by the consumer
    }
}

func main() {
    normalCh := make(chan int)      // Normal channel
    bufferedCh := make(chan int, 3) // Buffered channel with capacity 3

    fmt.Println("Using Normal Channel:")
    go producer(normalCh)
    consumer(normalCh)

    time.Sleep(2 * time.Second) // Wait for the producer to finish

    fmt.Println("\nUsing Buffered Channel:")
    go producer(bufferedCh)
    consumer(bufferedCh)
}
Using Normal Channel:
Producing 1
Consuming 1
Producing 2
Consuming 2
Producing 3
Consuming 3
Producing 4
Consuming 4
Producing 5
Consuming 5

Using Buffered Channel:
Producing 1
Consuming 1
Producing 2
Consuming 2
Producing 3
Producing 4
Consuming 3
Producing 5
Consuming 4
Consuming 5

YouTube频道:https://www.youtube.com/@codepiper
github链接:https://github.com/arshad404/CodePiper/tree/main/GO/concurrency-patterns/Basics