Golang中goroutine同步问题求助

Golang中goroutine同步问题求助 大家好,

我目前正在开发一个Go项目,goroutine的同步问题让我很困扰。由于我有多个goroutine在并发运行,我需要确保它们都能正确同步。

在多个goroutine共享同一资源的情况下,我希望确保同一时间只有一个goroutine可以访问该共享资源。虽然我听说过Go中的互斥锁(mutex)和通道(channel),但我不确定哪种策略最适合我的情况。

有人能提供关于在Go中正确同步goroutine的指导或示例吗?任何指导或帮助都将不胜感激。

提前感谢!

3 回复

这个 Go by Example: Mutexes 链接对你有帮助吗?

更多关于Golang中goroutine同步问题求助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


package main

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

var (
	resource int           // 共享资源
	mutex    sync.Mutex    // 用于同步的互斥锁对象
	wg       sync.WaitGroup // 用于goroutine同步的等待组
)

func main() {
	numWorkers := 5 // goroutine的数量
	wg.Add(numWorkers)

	for i := 0; i < numWorkers; i++ {
		go worker(i)
	}

	wg.Wait() // 等待所有goroutine完成
}

func worker(id int) {
	defer wg.Done()

	for i := 0; i < 5; i++ {
		// 开始对共享资源进行同步
		mutex.Lock()
		// 访问共享资源并执行操作
		resource++
		fmt.Printf("Worker %d: Resource value: %d\n", id, resource)
		// 结束同步
		mutex.Unlock()

		time.Sleep(time.Millisecond * 100) // 操作之间的延迟
	}
}

在给定的示例中,resource 变量表示由多个goroutine访问的共享资源。mutex 对象是一个用于同步的互斥锁,用于确保对共享资源的独占访问。wg 变量是一个 sync.WaitGroup,用于等待所有goroutine完成。

worker() 函数是每个goroutine执行的函数。它执行5次工作迭代。在执行工作之前,使用 mutex.Lock() 函数锁定互斥锁,防止其他goroutine访问资源。工作完成后,使用 mutex.Unlock() 函数解锁互斥锁,允许其他goroutine访问资源。

通过使用互斥锁来同步对共享资源的访问,可以避免goroutine之间的竞态条件,并保持数据的一致性。

在Go中,goroutine的同步通常可以通过互斥锁(sync.Mutex)或通道(channel)来实现。选择哪种方式取决于具体场景:互斥锁适合保护共享资源的临界区,而通道更适合goroutine间的通信和协调。以下分别提供两种方式的示例代码。

使用互斥锁(sync.Mutex)同步

当多个goroutine需要访问同一共享资源(如变量、数据结构)时,可以使用互斥锁确保同一时间只有一个goroutine进入临界区。

package main

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

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

func increment() {
    defer wg.Done()
    mu.Lock()
    defer mu.Unlock()
    temp := counter
    time.Sleep(1 * time.Millisecond)
    counter = temp + 1
}

func main() {
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go increment()
    }
    wg.Wait()
    fmt.Println("Final counter:", counter)
}

此示例中,sync.Mutex保护了counter变量的更新,避免了竞态条件。输出应为Final counter: 100

使用通道(channel)同步

通道可以用于goroutine间的通信,通过发送和接收操作隐式实现同步。以下示例使用通道控制对共享资源的访问,模拟类似互斥锁的行为。

package main

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

var (
    counter int
    wg      sync.WaitGroup
)

func increment(ch chan struct{}) {
    defer wg.Done()
    <-ch // 从通道接收,等待许可
    temp := counter
    time.Sleep(1 * time.Millisecond)
    counter = temp + 1
    ch <- struct{}{} // 发送回通道,释放许可
}

func main() {
    ch := make(chan struct{}, 1)
    ch <- struct{}{} // 初始化一个许可

    for i := 0; i < 100; i++ {
        wg.Add(1)
        go increment(ch)
    }
    wg.Wait()
    fmt.Println("Final counter:", counter)
}

此示例使用缓冲通道(容量为1)作为信号量,确保同一时间只有一个goroutine能获取许可并访问counter。输出同样为Final counter: 100

选择建议

  • 互斥锁:适用于简单的共享资源保护,代码直观,性能开销通常较低。
  • 通道:适用于更复杂的协调场景,如流水线、任务分发或超时控制。Go的哲学是“不要通过共享内存来通信,而应通过通信来共享内存”。

根据你的需求,如果只是保护共享资源,互斥锁是直接选择;如果需要更灵活的goroutine间协作,通道可能更合适。

回到顶部