在Go语言中使用互斥锁(mutexes)

huangapple go评论73阅读模式
英文:

Using mutexes in Go

问题

我正在尝试理解互斥锁的工作原理。根据我目前的理解,互斥锁用于执行原子操作并同步对某些数据的访问。

我在这里构建了一个队列数据结构的示例:https://github.com/arnauddri/algorithms/blob/master/data-structures%2Fqueue%2Fqueue.go

以下是代码的一部分:

package queue

import "sync"

type Queue struct {
    queue []interface{}
    len   int
    lock  *sync.Mutex
}

func New() *Queue {
    queue := &Queue{}
    queue.queue = make([]interface{}, 0)
    queue.len = 0

    return queue
}

func (q *Queue) Push(el interface{}) {
    q.lock.Lock()
    defer q.lock.Unlock()

    q.queue = append(q.queue, el)
    q.len++
}

然而,当我尝试创建一个队列并向其中添加一个元素时,我得到了一个运行时错误:

q := New()
q.Push(1)

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference

我真的不明白这里发生了什么。

我应该如何在这里使用互斥锁?

非常感谢。

英文:

I am trying to understand how mutexes work. From my understanding so far, it is made to carry atomic operations and synchronize access to some data.

I built an example of a queue data structure here: https://github.com/arnauddri/algorithms/blob/master/data-structures%2Fqueue%2Fqueue.go

Here is a bit of the code:

package queue

import "sync"

type Queue struct {
	queue []interface{}
	len   int
	lock  *sync.Mutex
}

func New() *Queue {
	queue := &Queue{}
	queue.queue = make([]interface{}, 0)
	queue.len = 0

	return queue
}

func (q *Queue) Push(el interface{}) {
	q.lock.Lock()
	defer q.lock.Unlock()

	q.queue = append(q.queue, el)
	q.len++
}

However when I try to create a queue and push an item to it I get a runtime error:

q := New()
q.Push(1)

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference

I really don't understand what is happening here.

How should I use the Mutex here?

Many thanks

答案1

得分: 19

你之所以出现这个错误,是因为你没有分配任何互斥锁,你只有一个指向互斥锁的指针。
互斥锁通常在结构体内部声明,而且不需要使用指针。请参考下面的工作示例:

import "sync"

type Queue struct {
    len int

    lock  sync.Mutex // 在被互斥锁保护的字段之前添加它
    queue []interface{}
}

func New() *Queue {
    queue := &Queue{}
    queue.queue = make([]interface{}, 0)
    queue.len = 0

    return queue
}

func (q *Queue) Push(el interface{}) {
    q.lock.Lock()
    defer q.lock.Unlock()

    q.queue = append(q.queue, el)
    q.len++
}

func main() {
    q := New()
    q.Push(1)
}

希望对你有帮助!

英文:

You get that error because you did not allocated any mutex, you have only a pointer to a mutex.
Mutex usually are declared inside the structure and without the pointer. See working example below:

http://play.golang.org/p/8LF3yVOkSW

import "sync"

type Queue struct {
	len int

	lock  sync.Mutex // add it before the fields that are being protected by the mutex
	queue []interface{}
}

func New() *Queue {
	queue := &Queue{}
	queue.queue = make([]interface{}, 0)
	queue.len = 0

	return queue
}

func (q *Queue) Push(el interface{}) {
	q.lock.Lock()
	defer q.lock.Unlock()

	q.queue = append(q.queue, el)
	q.len++
}

func main() {
	q := New()
	q.Push(1)
}

答案2

得分: 7

看起来问题是你从未实例化互斥锁。当你运行New()函数时,你创建了一个空的队列,并且有一个可以引用互斥锁的变量,但你从未告诉它实际上要这样做,这意味着此时queue.lock == nil。你可以通过在New()函数中添加一个实例化语句来解决这个问题。

queue.lock = new(sync.Mutex)

这里有一个可以工作的示例:http://play.golang.org/p/Qa6buDaHIj

英文:

Looks like the issue is that you're never instantiating the mutex. When you run the New() function you're creating an empty Queue with a variable that can reference a mutex, but you never actually tell it to do so, which means that at this point queue.lock == nil. You can fix this by adding in an instantiation line to your New() function.

queue.lock = new(sync.Mutex)

Here is a playground demo that works: http://play.golang.org/p/Qa6buDaHIj

答案3

得分: 2

指针的零值是nil,q.lock是一个nil指针,解引用nil指针会导致恐慌。
你可以使用sync.Mutex而不是*sync.Mutex,Mutex的零值是一个未锁定的互斥锁。
结构体的匿名嵌套也可以解决你的问题:

package queue

import "sync"

type Queue struct {
    queue []interface{}
    len   int
    sync.Mutex
}

func New() *Queue {
    queue := &Queue{}
    queue.queue = make([]interface{}, 0)
    queue.len = 0

    return queue
}

func (q *Queue) Push(el interface{}) {
    q.Lock()
    defer q.Unlock()

    q.queue = append(q.queue, el)
    q.len++
}
英文:

Zero value for a pointer is nil, q.lock is a nil pointer, dereference nil pointer will cause panic.
You can use lock sync.Mutex instead of *lock sync.Mutex, zero value for a Mutex is an unlocked mutex.
Struct anonymous nesting can also solve your problem:

package queue

import "sync"

type Queue struct {
    queue []interface{}
    len   int
    sync.Mutex
}

func New() *Queue {
    queue := &Queue{}
    queue.queue = make([]interface{}, 0)
    queue.len = 0

    return queue
}

func (q *Queue) Push(el interface{}) {
    q.Lock()
    defer q.Unlock()

    q.queue = append(q.queue, el)
    q.len++
}

huangapple
  • 本文由 发表于 2015年1月31日 03:41:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/28243409.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定