golang锁面试题

发布时间:2024-12-22 19:59:16

在并发编程中,锁是一种常用的机制,用于保护共享资源不被多个线程同时修改。对于Golang开发者来说,对锁的理解和使用是非常重要的。本文将深入探讨Golang中的锁以及与之相关的面试题目。

Golang中的锁类型及其使用

在Golang中,常用的锁有两种类型:互斥锁(Mutex)和读写锁(RWMutex)。互斥锁用于保护共享资源的互斥访问,即同一时间只允许一个线程对共享资源进行读写操作。而读写锁则提供了更灵活的读写策略,允许多个线程同时读取共享资源,但只允许一个线程进行写操作。

在使用锁的过程中,需要注意以下几点:

  1. 锁的获取和释放应该成对出现,确保程序正确地进入和退出临界区。
  2. 锁的粒度应该尽量小,避免不必要的锁竞争,提高并发性能。
  3. 使用defer语句在函数返回时自动释放锁,防止由于异常情况导致锁无法被正常释放。

Golang锁面试题一:互斥锁

互斥锁是最基本的锁类型,在Golang中通过sync包提供的Mutex类型实现。下面是一个关于互斥锁的面试题目:

问题:下面的代码有没有问题?如果有问题,应该如何修改?

package main

import (
	"fmt"
	"sync"
)

var m sync.Mutex
var counter = 0

func main() {
	wg := sync.WaitGroup{}
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			m.Lock()
			counter++
			m.Unlock()
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("counter:", counter)
}

答案:上述代码存在问题,因为counter变量可能会被多个goroutine同时修改,从而导致数据竞争。为了解决这个问题,可以使用互斥锁将对counter的修改操作变为互斥的。修改后的代码如下:

package main

import (
	"fmt"
	"sync"
)

var m sync.Mutex
var counter = 0

func main() {
	wg := sync.WaitGroup{}
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			m.Lock()
			counter++
			m.Unlock()
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("counter:", counter)
}

Golang锁面试题二:读写锁

读写锁是一种更高级的锁类型,在Golang中通过sync包提供的RWMutex类型实现。下面是一个关于读写锁的面试题目:

问题:下面的代码有没有问题?如果有问题,应该如何修改?

package main

import (
	"fmt"
	"sync"
)

var rw sync.RWMutex
var counter = 0

func main() {
	wg := sync.WaitGroup{}
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			rw.Lock()
			counter++
			rw.Unlock()
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("counter:", counter)
}

答案:上述代码存在问题,因为读写锁应该用于保护共享资源的读写操作,但是在上述代码中,我们仅仅对counter进行了写操作,并没有进行任何读操作。因此,上述代码中使用读写锁是不合适的。如果要修复这个问题,可以将互斥锁替换为普通的互斥锁(Mutex)。修改后的代码如下:

package main

import (
	"fmt"
	"sync"
)

var m sync.Mutex
var counter = 0

func main() {
	wg := sync.WaitGroup{}
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			m.Lock()
			counter++
			m.Unlock()
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("counter:", counter)
}

Golang锁面试题三:死锁问题

在并发编程中,死锁是一个非常常见的问题。下面是一个关于死锁的面试题目:

问题:下面的代码有没有问题?如果有问题,应该如何修改?

package main

import (
	"fmt"
	"sync"
)

var m1 sync.Mutex
var m2 sync.Mutex

func main() {
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		m1.Lock()
		m2.Lock()
		fmt.Println("goroutine 1")
		m1.Unlock()
		m2.Unlock()
		wg.Done()
	}()
	go func() {
		m2.Lock()
		m1.Lock()
		fmt.Println("goroutine 2")
		m2.Unlock()
		m1.Unlock()
		wg.Done()
	}()
	wg.Wait()
}

答案:上述代码存在死锁问题。死锁的原因是两个goroutine互相等待对方释放锁。为了解决这个问题,可以使用多个锁的顺序一致性原则,即按照指定的顺序获得和释放锁。修改后的代码如下:

package main

import (
	"fmt"
	"sync"
)

var m1 sync.Mutex
var m2 sync.Mutex

func main() {
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		m1.Lock()
		m2.Lock()
		fmt.Println("goroutine 1")
		m2.Unlock()
		m1.Unlock()
		wg.Done()
	}()
	go func() {
		m1.Lock()
		m2.Lock()
		fmt.Println("goroutine 2")
		m2.Unlock()
		m1.Unlock()
		wg.Done()
	}()
	wg.Wait()
}

通过上述修改,保证了goroutine 1和goroutine 2获取锁的顺序一致,解决了死锁问题。

相关推荐