简单队列模型示例

时间:2018-03-15 09:52:23

标签: multithreading go queue

是否有一个简单的程序来演示Go中队列的工作方式。 我只需要在队列中添加1到10之类的东西,并使用另一个线程并行地从队列中拉出那些。

3 个答案:

答案 0 :(得分:7)

对于并发使用安全的队列基本上是一种语言构造:channel

渠道设计 - 对于并发sendreceive是安全的。这是详细信息:If I am using channels properly should I need to use mutexes?在其上发送的值按发送顺序接收。

您可以在此处详细了解频道:What are golang channels used for?

一个非常简单的例子:

c := make(chan int, 10) // buffer for 10 elements

// Producer: send elements in a new goroutine
go func() {
    for i := 0; i < 10; i++ {
        c <- i
    }
    close(c)
}()

// Consumer: receive all elements sent on it before it was closed:
for v := range c {
    fmt.Println("Received:", v)
}

输出(在Go Playground上尝试):

Received: 0
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Received: 6
Received: 7
Received: 8
Received: 9

请注意,通道缓冲区(本例中为10)与要通过它“发送”的元素数无关。缓冲区告诉通道可以“存储”多少元素,换句话说,当没有人从中接收时,可以在没有阻塞的情况下发送多少元素。当通道的缓冲区已满时,进一步的发送将阻塞,直到有人开始从中接收值。

答案 1 :(得分:3)

您可以使用频道(安全用于并发使用)和等待组同时从队列中读取

package main

import (
    "fmt"
    "sync"
)

func main() {
    queue := make(chan int)

    wg := new(sync.WaitGroup)
    wg.Add(1)
    defer wg.Wait()

    go func(wg *sync.WaitGroup) {
        for {

            r, ok := <-queue
            if !ok {
                wg.Done()
                return
            }

            fmt.Println(r)
        }
    }(wg)

    for i := 1; i <= 10; i++ {
        queue <- i
    }

    close(queue)
}

游乐场链接:https://play.golang.org/p/A_Amqcf2gwU

答案 2 :(得分:0)

另一种选择是创建和实现队列interface,其支持类型为并发通道。为方便起见,我做了gist

以下是如何使用它。

queue := GetIntConcurrentQueue()
defer queue.Close()

// queue.Enqueue(1)
// myInt, errQueueClosed := queue.DequeueBlocking()
// myInt, errIfNoInt := queue.DequeueNonBlocking()

这里有更长的例子 - https://play.golang.org/p/npb2Uj9hGn1

下面的完整实现,这里再次是gist

// Can be any backing type, even 'interface{}' if desired.
// See stackoverflow.com/q/11403050/3960399 for type conversion instructions.
type IntConcurrentQueue interface {
    // Inserts the int into the queue
    Enqueue(int)
    // Will return error if there is nothing in the queue or if Close() was already called
    DequeueNonBlocking() (int, error)
    // Will block until there is a value in the queue to return.
    // Will error if Close() was already called.
    DequeueBlocking() (int, error)
    // Close should be called with defer after initializing
    Close()
}

func GetIntConcurrentQueue() IntConcurrentQueue {
    return &intChannelQueue{c: make(chan int)}
}

type intChannelQueue struct {
    c chan int
}

func (q *intChannelQueue) Enqueue(i int) {
    q.c <- i
}

func (q *intChannelQueue) DequeueNonBlocking() (int, error) {
    select {
    case i, ok := <-q.c:
        if ok {
            return i, nil
        } else {
            return 0, fmt.Errorf("queue was closed")
        }
    default:
        return 0, fmt.Errorf("queue has no value")
    }
}

func (q *intChannelQueue) DequeueBlocking() (int, error) {
    i, ok := <-q.c
    if ok {
        return i, nil
    }
    return 0, fmt.Errorf("queue was closed")
}

func (q *intChannelQueue) Close() {
    close(q.c)
}