如何在Go中创建共享队列?

如何在Go中创建共享队列?,go,concurrency,parallel-processing,priority-queue,goroutine,Go,Concurrency,Parallel Processing,Priority Queue,Goroutine,我正在尝试为负载平衡器实现最小连接算法。我使用优先级队列来保持每台服务器的连接数按排序顺序排列。 代码如下: server = spq[0] serverNumber = server.value updatedPriority = server.priority + 1 // Increment connection count for server spq.update(server, serverNumber, updatedPriority) targetUrl, err := u

我正在尝试为负载平衡器实现最小连接算法。我使用优先级队列来保持每台服务器的连接数按排序顺序排列。 代码如下:

server = spq[0]
serverNumber = server.value

updatedPriority = server.priority + 1 // Increment connection count for server

spq.update(server, serverNumber, updatedPriority)

targetUrl, err := url.Parse(configuration.Servers[serverNumber])
if err != nil {
    log.Fatal(err)
}

// Send the request to the selected server
httputil.NewSingleHostReverseProxy(targetUrl).ServeHTTP(w, r)

updatedPriority = server.priority - 1 // Decrement connection count for server
spq.update(server, serverNumber, updatedPriority)
其中
spq
是我的优先级队列

此代码将针对平衡器将收到的每个请求运行。 但在记录每个请求的队列状态后,我没有得到正确的结果。 例如,在一个例子中,我看到队列包含同一服务器两次,优先级不同


我确信这与跨请求同步和锁定队列有关。但我不确定在这种特殊情况下,什么是正确的方法。

如果这真的是在多个goroutine中运行的代码,那么很明显,您有race

我不理解spq.update。一开始,它看起来像是一个重新排序队列的函数,以使服务器在元素0处具有最少的调用次数,但是为什么它同时需要server和serverNumber呢?serverNumber似乎是服务器的唯一ID,既然您已经拥有服务器,为什么需要它


在任何情况下,您都应该拥有一个由所有goroutine共享的sync.Mutex,并在第一行之前锁定互斥体,在spq.update之后解锁,您还应该在代理调用之后再次锁定它,并在所有操作完成后解锁。从server.priority中减去1的行仅当server是指针时才起作用。如果不是指针,则您将丢失呼叫期间对服务器的所有更新。

这似乎是队列的一个奇怪用例-队列用于将项目推到一端并从一端弹出的列表。你用它来“保持每台服务器的连接数”,这看起来像是一个映射的工作,或者可能只是一个服务器结构,带有一个用于连接数的字段。这是肯定的。这里没有任何东西阻止两个并发请求同时选择spq[0],而第三个调用更新,然后对优先级字段的更新再次进行。使用-race编译,不久就会崩溃。您必须以原子方式执行所有这些操作:选择一台服务器,更新其优先级,重新排序队列。您没有说明要处理多少台服务器,但如果数量较少,只需使用映射(或切片)和互斥锁即可。如果迭代变得昂贵,你真的只需要优先级队列,这在我看来是不可能的。@Adrian它是一个优先级队列,可以让列表保持排序,我可以很容易地得到连接最少的服务器。我认为这将是优先级队列的一个用例。在请求期间,您不需要持有锁。抓住锁,进行选择并重新排序,释放锁,处理请求,锁定,再次重新排序,解锁。我正在使用优先级队列的这个实现。我想你是对的,这个方法不需要ID和服务器。我会像你说的那样尝试使用sync.Mutex并在这里更新它。所以服务器是一个指针。如果您像我所描述的那样添加互斥锁,它应该可以消除这里的竞争。在访问队列的任何位置,都必须使用相同的互斥锁来锁定以防止任何竞争。您可能需要研究使用此堆队列: