并发队列返回通道,锁定疑虑

时间:2018-06-15 10:13:45

标签: go concurrency locking

有一些不重要的结构Message的队列,它有经典的push和pop方法:

type Queue struct {
    messages list.List
}

//The implementation is not relevant for the sake of the question
func (q *Queue) Push(msg Message) { /*...*/ }
func (q *Queue) Pop() (Message, bool) { /*...*/ }

/*
 * NewTimedChannel runs a goroutine which pops a message from the queue every 
 * given time duration and sends it over the returned channel 
 */
func (q *Queue) NewTimedChannel(t time.Duration) (<-chan Message) {/*...*/}

Push函数的客户端将成为用户将发布消息的web gui NewTimedChannel返回的信道的客户端将是通过网络将每个消息发送到不相关的端点的服务。

我是并发的新手,我有以下问题:

我知道,因为Queue.messages是主要goroutine之间的共享状态,它处理在用户提交Web表单后推送消息以及为每个NewTimedChannel调用创建的消息,我需要锁定它。

我是否需要使用所有Push,Pop和NewTimedChannel方法中的sync.Mutex锁定和解锁?
是否有一种更惯用的方式来处理go环境中的这个特定问题?

2 个答案:

答案 0 :(得分:0)

  

我是否需要在所有Push,Pop和NewTimedChannel方法中使用sync.Mutex锁定和解锁?

  

是否有一种更惯用的方式来处理这个特定的问题   去环境?

有关见解,请查看此问题的最后一个答案:

How do I (succinctly) remove the first element from a slice in Go?

答案 1 :(得分:0)

正如其他人所指出的,它需要同步或者会有数据竞争。

在Go中有一种说法,&#34;不要通过共享内存进行通信,通过通信共享内存。&#34;在这种情况下,我认为一种自觉的方式是将频道发送到一个单独的goroutine,它使用select将所有操作同步到一起。通过添加更多通道来支持更多类型的操作(例如代码中的定时通道,我不完全了解它的作用),以及使用select和其他工具,可以轻松扩展代码,它可以通过使用锁轻松处理更复杂的同步。我写了一些示例代码:

type SyncQueue struct {
    Q AbsQueue
    pushCh,popMsgCh chan Message
    popOkCh chan bool
    popCh chan struct{}
}

// An abstract of the Queue type. You can remove the abstract layer.
type AbsQueue interface {
    Push(Message)
    Pop() (Message,bool)
} 

func (sq SyncQueue) Push(m Message) {
    sq.pushCh <- m
}

func (sq SyncQueue) Pop() (Message,bool) {
    sq.popCh <- struct{}{} // send a signal for pop. struct{}{} cost no memory at all.

    return <-sq.popMsgCh,<-sq.popOkCh
}

// Every pop and push get synchronized here.
func (sq SyncQueue) Run() {
    for {
        select {
        case m:=<-pushCh:
            Q.Push(m)
        case <-popCh:
            m,ok := Q.Pop()
            sq.popMsgCh <- m
            sq.popOkCh <- ok
        }   
    }
}

func NewSyncQueue(Q AbsQueue) *SyncQueue {
    sq:=SyncQueue {
        Q:Q,
        pushCh: make(chan Message),popMsgCh: make(chan Message),
        pushOkCh: make(chan bool), popCh: make(chan struct{}),
    }
    go sq.Run()
    return &sq 
}

请注意,对于简单性,我没有使用退出通道或context.Context,因此sq.Run()的goroutine无法退出并导致内存泄漏。