MST

星途 面试题库

面试题:Go语言中原子操作与通道结合保证复杂场景线程安全的设计与实现

假设存在一个复杂的场景,需要多个goroutine对一组共享数据进行复杂的读写操作,既要保证数据的一致性,又要尽可能提高并发性能。请设计一个基于Go语言原子操作和通道的解决方案,并详细阐述设计思路、关键代码实现以及如何确保线程安全性。
41.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 原子操作:对于简单的数据类型(如整数、指针等),使用Go语言的原子包(sync/atomic)进行操作,以保证在多goroutine环境下数据的一致性。
  2. 通道(Channel):用于在goroutine之间传递数据和同步。通过通道来协调读写操作,避免数据竞争。
  3. 读写锁:对于更复杂的数据结构,使用读写锁(sync.RWMutex)。读操作可以并发执行,写操作则需要独占锁,以保证数据一致性。

关键代码实现

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

// 假设共享数据为一个整数
var sharedData int64

// 用于同步的通道
var readCh = make(chan struct{})
var writeCh = make(chan struct{})

func reader(wg *sync.WaitGroup, id int) {
    defer wg.Done()
    readCh <- struct{}{} // 通知可以开始读操作
    data := atomic.LoadInt64(&sharedData)
    <-readCh // 通知读操作完成
    fmt.Printf("Reader %d read data: %d\n", id, data)
}

func writer(wg *sync.WaitGroup, id int, newData int64) {
    defer wg.Done()
    writeCh <- struct{}{} // 通知可以开始写操作
    atomic.StoreInt64(&sharedData, newData)
    <-writeCh // 通知写操作完成
    fmt.Printf("Writer %d wrote data: %d\n", id, newData)
}

确保线程安全性

  1. 原子操作:通过atomic.LoadInt64atomic.StoreInt64函数,保证对sharedData的读写操作是原子的,不会出现数据竞争。
  2. 通道同步:读操作和写操作通过通道readChwriteCh进行同步。读操作开始前向通道发送信号,完成后从通道接收信号;写操作同理。这样可以确保读写操作不会同时进行,保证数据一致性。
  3. 读写锁(如果使用复杂数据结构):如果共享数据是复杂数据结构,在读写操作前获取相应的读写锁。读操作获取读锁,允许多个读操作并发;写操作获取写锁,独占数据,防止其他读写操作同时进行。

在主函数中使用示例:

func main() {
    var wg sync.WaitGroup

    // 启动多个读操作
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go reader(&wg, i)
    }

    // 启动多个写操作
    for i := 0; i < 2; i++ {
        wg.Add(1)
        go writer(&wg, i, int64(i*10))
    }

    wg.Wait()
}

这样通过原子操作、通道同步以及读写锁(根据实际情况),可以在多goroutine环境下保证共享数据的一致性和线程安全性,同时尽可能提高并发性能。