You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言多通道读写顺序:无缓冲通道结合信号量控制Goroutine并发问题

Go Channel Read/Write Order & Concurrency Control Analysis for Your Scenario

Let's walk through your code and break down the channel behavior, concurrency control logic, and potential pitfalls you should watch out for.

1. Semaphore Channel (sem) Read/Write Order

First, let's clarify: your sem is a buffered channel with capacity 10, not an unbuffered one. This is the standard way to implement a concurrency limiter in Go, and here's how its read/write order works:

  • When a goroutine executes sem <- struct{}{}:
    • If the channel has empty buffer slots (fewer than 10 values already sent), this write operation completes immediately—no blocking.
    • Once the buffer is full (10 goroutines have acquired the semaphore), any additional goroutine trying to write to sem will block. These blocked goroutines are queued in FIFO (first-in, first-out) order.
  • When a goroutine exits, the defer func() { <-sem }() runs: this reads one value from the channel, freeing up a buffer slot. The first goroutine that was blocked on writing to sem will be woken up immediately to acquire the newly freed slot.

This FIFO behavior ensures that goroutines get access to the semaphore in the order they requested it, preventing starvation in most cases.

2. Concurrency Control Logic Breakdown

WaitGroup Usage

Your current WaitGroup setup only tracks a single goroutine (since you call wg.Add(1) once in main and only invoke DoSomething once). This works fine if DoSomething doesn't spawn additional goroutines, but it's a problem if you plan to recursively call DoSomething for the newSomethings you generate:

  • If you loop through newSomethings and start new DoSomething goroutines, you must call wg.Add(1) before starting each new goroutine (otherwise, main's wg.Wait() will exit as soon as the first DoSomething finishes, killing all in-progress goroutines).

For example, if you process newSomethings like this:

for _, url := range newSomethings {
    wg.Add(1)
    go DoSomething(url, sem, wg)
}

This ensures every spawned goroutine is tracked by the WaitGroup.

Semaphore Safety

Your semaphore release logic is solid:

  • The defer func() { <-sem }() runs after the task completes (even if the function panics), so you'll never leak a semaphore slot. This is critical—leaking slots would gradually reduce your available concurrency until no more goroutines can run.

3. Key Notes & Potential Fixes

  • Don't confuse buffered vs unbuffered channels: Your semaphore uses a buffered channel, which is correct for limiting concurrency to 10. An unbuffered channel would only allow 1 concurrent goroutine (since writes block until a read happens).
  • WaitGroup scope: Make sure the same WaitGroup instance is passed to all spawned DoSomething goroutines so they can all signal their completion.
  • Error handling: If your task (the //...... part) can return errors, consider adding error handling—right now, any panic would be recovered by the defer (since defer runs even on panic), but you might want to log or handle errors explicitly.

内容的提问来源于stack exchange,提问作者dm03514

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:21:15