关于Go语言Channel与Buffered Channel差异的技术咨询
Hey there! Let's clear up this confusion step by step—this is a super common gotcha when first learning Go channels, so you're not alone.
First, let's bust the myth: unbuffered channels are strictly FIFO (first-in, first-out). The "LIFO" order you're seeing isn't the channel's fault—it's all about how Go's goroutine scheduler works.
Why Your Example Shows Reverse Order
Let's start with your code snippet (I'll fill in the missing parts to make it complete):
package main import "fmt" func sum(s []int, c chan int) { total := 0 for _, num := range s { total += num } c <- total // Send the sum to the channel } func main() { c := make(chan int) go sum([]int{7, 2, 8}, c) go sum([]int{-9, 4, 0}, c) x := <-c y := <-c fmt.Println(x, y) // Sometimes outputs -5 17, sometimes 17 -5 }
When you launch two goroutines with go sum(...), Go's scheduler doesn't guarantee which one runs first or finishes first. The sum of [-9,4,0] is quicker to calculate (it's just adding three small numbers with no large intermediate sums), so that goroutine might finish first and send -5 to the channel before the other goroutine sends 17.
The channel itself will always deliver values in the order they were sent—so if -5 goes in first, it comes out first. If you run this program multiple times, you might even see the order flip occasionally, depending on how the scheduler prioritizes the goroutines. That's totally normal!
Core Differences: Unbuffered vs Buffered Channels
Now let's break down the key distinctions between the two channel types:
Unbuffered Channels (make(chan int))
- Strictly synchronous: When you send a value to an unbuffered channel, the sending goroutine blocks until another goroutine receives that value. Similarly, a receiving goroutine blocks until a value is sent to the channel.
- Think of it like passing a ball directly between two people—both have to be ready to catch/throw at the same time.
- Use case: Perfect for signaling between goroutines (e.g., telling a worker to stop, or confirming a task is done).
Buffered Channels (make(chan int, N) where N is the buffer size)
- Asynchronous (up to buffer limit): The channel has a fixed-size buffer. Sending a value only blocks if the buffer is full; receiving only blocks if the buffer is empty.
- Think of it like putting balls into a basket—you can keep adding balls until the basket is full, and someone can pick them out later without you waiting around.
- Use case: Great for producer-consumer patterns, where you might have multiple producers sending data that's processed by consumers at their own pace.
Quick Example of Buffered Channels
To see the difference, let's modify your code to use a buffered channel:
func main() { c := make(chan int, 2) // Buffer size of 2 sum([]int{7, 2, 8}, c) // No goroutine needed here—send won't block sum([]int{-9, 4, 0}, c) x := <-c y := <-c fmt.Println(x, y) // Will always output 17 -5, since we sent in order }
Here, we don't even need goroutines for the sum functions because the buffer can hold both values. Since we send 17 first and -5 second, the channel will deliver them in that exact order every time.
Wrap-Up
- Unbuffered channels enforce strict synchronization between senders and receivers.
- Buffered channels let you decouple senders and receivers up to the buffer size.
- The "reverse order" in your original code is due to goroutine scheduling, not the channel's behavior—channels are always FIFO.
内容的提问来源于stack exchange,提问作者shapeare

