为什么ZeroMQ上下文没有在所有goroutine之间共享?

时间:2017-02-03 23:30:13

标签: multithreading sockets go zeromq

在此ZeroMQ example中,

// Multithreaded Hello World server.
// Uses Goroutines.  We could also use channels (a native form of
// inproc), but I stuck to the example.
//
// Author:  Brendan Mc.
// Requires: http://github.com/alecthomas/gozmq

package main

import (
    "fmt"
    zmq "github.com/alecthomas/gozmq"
    "time"
)

func main() {
    // Launch pool of worker threads
    for i := 0; i != 5; i = i + 1 {
        go worker()
    }

    // Prepare our context and sockets
    context, _ := zmq.NewContext()
    defer context.Close()

    // Socket to talk to clients
    clients, _ := context.NewSocket(zmq.ROUTER)
    defer clients.Close()
    clients.Bind("tcp://*:5555")

    // Socket to talk to workers
    workers, _ := context.NewSocket(zmq.DEALER)
    defer workers.Close()
    workers.Bind("ipc://workers.ipc")

    // connect work threads to client threads via a queue
    zmq.Device(zmq.QUEUE, clients, workers)

}

func worker() {
    context, _ := zmq.NewContext()
    defer context.Close()

    // Socket to talk to dispatcher
    receiver, _ := context.NewSocket(zmq.REP)
    defer receiver.Close()
    receiver.Connect("ipc://workers.ipc")

    for true {
        received, _ := receiver.Recv(0)
        fmt.Printf("Received request [%s]\n", received)

        // Do some 'work'
        time.Sleep(time.Second)

        // Send reply back to client
        receiver.Send([]byte("World"), 0)
    }
}

每个goroutine都有自己的ZeroMQ Context 。但是,在ZeroMQ guide中,它说明了以下内容:

  

在流程开始时创建一个ZeroMQ上下文,并传递它   到你想通过inproc套接字连接的所有线程。

     

不要在线程之间共享ZeroMQ套接字。 ZeroMQ套接字不是   线程安全的。从技术上讲,可以从一个套接字迁移套接字   线程到另一个,但它需要技巧。它唯一的地方   远程理智,以便在线程之间共享套接字   需要像套接字上的垃圾收集那样做魔术的绑定。

我知道goroutines不是线程。

相反,他们生活在线程中。但是我也读过,在goroutines之间有一个共享对象是可能的。

那么,为什么goroutines之间没有共享上下文?

我认为它会占用更少的空间,因为其中包含套接字的上下文可能有点大。

但即使它不那么消耗,为什么在这个例子中没有共享上下文?

2 个答案:

答案 0 :(得分:0)

只要这些对象是线程安全的,您就可以在Goroutines之间共享对象。您正在使用的Go ZeroMQ绑定显然是非线程安全的。虽然单个Goroutine可能不对应于单个OS线程,但是不可能保证您生成的Goroutines将存在于同一个OS线程中。它们的生命周期由Go运行时动态管理。

你可以"分享"使用This excellent article on avoiding null同步访问权限,或使用channels,在Goroutine访问套接字时锁定对套接字的访问权限,在多个Goroutines之间使用ZeroMQ套接字。

答案 1 :(得分:0)

问:为什么不在goroutines之间共享上下文?(在示例中)

除了(相当极端)技术选项以确实共享一个Context实例(在一些合理的情况/场合下),所展示的代码片段专注于零共享,作为一般原则,ZeroMQ是零极限之一,ZeroMQ是众所周知的。

习惯使用一组分离的 Context 实例(其中一个也可能在同一个帖子中也有其中一些实例)是有利的。性能扩展:

  • 从广义上讲,工作负载分离,实例

  • 以更窄的含义,直接IO线程扩展(在Context实例化时定义不同数量的IO线程,一些只有1个IO线程,而其他可能有2个,3个或10个IO线程对于高性能数据泵引擎)并通过基于 setsockopt( ZMQ_AFFINITY ... ) 的套接字到IO线程映射使用位掩码映射,这允许直接分离某些传输 - 通过使用 {{1}的单独专用组,可以将任务分配到不同的 Context - 实例,还可以更具体地处理不同的传输流量优先级模式} -instances' IO线程(当然,故意配备了更多不同数量的底层ZeroMQ Context IO线程)。这对于性能扩展和几乎确定的流量策略处理实现来说是非常宝贵的功能。

这可能会激发您进一步思考这种细粒度管理(粒度)如何帮助您的分布式应用程序更好地利用ZeroMQ框架的服务,以及这种零共享概念如何为您打开性能扩展的新世界和吞吐量限制。

如果有疑问,请注意源代码中的初始注释:

Context

... // Uses Goroutines. We could also use channels (a native form of // inproc), but I stuck to the example. ... 传输类是如何从非共享,非阻塞上下文实例中受益并享受几乎线性扩展的明显示例,直到最佳表现。