在单独的进程(多处理)上运行Goroutines

时间:2018-06-28 21:33:55

标签: go multiprocessing goroutine

我目前有一个MQTT代码,可以订阅一个主题,打印出收到的消息,然后发布有关新主题的进一步说明。 订阅/打印在一个Goroutine中完成,而发布在另一个Goroutine中完成。这是我的代码:

var wg, pg sync.WaitGroup
// All messages are handled here - printing published messages and publishing new messages
var f MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) {

wg.Add(1)
pg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Printf("%s\n", msg.Payload())
        //fmt.Println(os.Getpid())
    }()
go func(){
    defer pg.Done()
    message := ""
    //Changing configurations
    if strings.Contains(string(msg.Payload()), "arduinoLED") == true {
        message = fmt.Sprintf("change configuration")
    }
    if  strings.Contains(string(msg.Payload()), "NAME CHANGED") == true{
        message = fmt.Sprintf("change back")
    }
    // Publish further instructions to "sensor/instruction"
    token := client.Publish("sensor/instruction", 0, false, message)
    //fmt.Println(os.Getpid())
    token.Wait()

}()
}

func main() {

    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)

    opts := MQTT.NewClientOptions().AddBroker("tcp://test.mosquitto.org:1883")

    opts.SetDefaultPublishHandler(f)
    // Topic to subscribe to for sensor data
    topic := "sensor/data"

    opts.OnConnect = func(c MQTT.Client) {
        if token := c.Subscribe(topic, 0, f); token.Wait() && token.Error() != nil {
            panic(token.Error())
        }
    }
    // Creating new client
    client := MQTT.NewClient(opts)
    if token := client.Connect(); token.Wait() && token.Error() != nil {
        panic(token.Error())
    } else {
        fmt.Printf("Connected to server\n")
    }
    wg.Wait()
    pg.Wait()
    <-c
}

已注释掉的os.Getpid()行用于检查我正在运行哪个Goroutine的进程。现在它们都显示相同的数字(这意味着它们都在同一进程上运行?)。

我的问题是:如何在单独的进程上运行两个Goroutine?有办法吗?

编辑::如果无法完成此操作,我想使用渠道编写此代码。这是我的代码:

var f MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) {
    sensorData := make(chan []byte)
wg.Add(1)
pg.Add(1)
    go func() {
        defer wg.Done()
        //fmt.Printf("%s\n", msg.Payload())
        sensorData <- string(msg.Payload())
        fmt.Println(<-sensorData) //currently not printing anything
    }()
go func(){
    defer pg.Done()
    message := ""
    //Changing configurations
    if strings.Contains(<-sensorData, "arduinoLED") == true{
        message = fmt.Sprintf("change configuration")
    }
    if strings.Contains(<-sensorData, "NAME CHANGED") == true{
        message = fmt.Sprintf("change back")
    }
    // Publish further instructions to "sensor/instruction"
    token := client.Publish("sensor/instruction", 0, false, message)
    token.Wait()

}()

}

但是,我无法使用通道打印任何数据。我在做什么错了?

2 个答案:

答案 0 :(得分:5)

您可能来自Python,对吗? ;-)

它的模块名为 multiprocessing 在其stdlib中,这很可能会解释您为什么使用 您的问题标题中的这个名称,以及为什么您显然是 在解释@JimB的意思时遇到了困难

  

如果您需要单独的流程,则需要自己执行

Python中的“多处理”

问题是,Python的multiprocessing是相当高级的 藏在里面的东西很多。 当您生成multiprocessing.Process并使其运行时 一个函数,真正发生的是这样:

  1. Python解释器创建另一个操作系统的 处理(使用 fork(2)在类Unix系统上 或Windows上的CreateProcess)并安排 也可以执行Python插入器。

    关键点是您现在将有两个流程 运行两个Python插入器。

  2. 它是为Python插入程序安排的 子进程有一种与Python通信的方法 父进程中的解释器。

    此“通信链接” 涉及某种形式 IPC中@JimB的引用。 根本没有其他方法可以传达数据和动作 完全是因为商品 现代操作系统提供了严格的流程分离。

  3. exchange Python objects between the processes时,两个通信的Python 口译员将它们 serialize deserialize 放在背后 通过IPC链接发送之前以及接收之后 他们从那里,相应地。 这是使用pickle模块实现的。

返回开始

Go没有任何可以直接解决的直接解决方案 与Python的multiprocessing匹配,我真的怀疑它是否可以 已经明智地实施了。

主要原因是Go 比Python低得多,因此它没有 拥有Python的丰富经验,可以对 它所管理的价值观的类型,并且还努力拥有 尽可能减少其构造中的隐藏成本。

Go还努力避免采用“框架式”方法 解决问题,并在使用“图书馆式”解决方案时 可能。 (“框架与库”的摘要 例如here。) Go的所有内容都在其标准库中实现 类似于Python的multiprocessing,但没有 现成的frakework-y解决方案。

因此,您可以按照以下步骤进行操作:

  1. 使用os/exec运行您自己的进程的另一个副本。

    • 确保产生的进程“知道”它已启动 在特殊的“奴隶”模式下进行相应操作。
    • 使用任何形式的IPC与新流程进行通信。 通过standard I/O streams交换数据 子过程应该是 最简单的滚动方式(除非您需要交换 已打开的文件,但这是一个比较麻烦的话题,所以我们不要离题。)
  2. 使用encoding/层次结构中的任何程序包进行序列化 并在交换时反序列化数据。

    “定位”解决方案据说是encoding/gob

  3. 发明并实施一个简单的协议来告知 子进程该做什么,用什么数据, 以及如何将结果传达回母版。

真的值得麻烦吗?

出于很多原因,我会说不,不是:

  • Go像可怕的GIL一样, 因此无需回避它即可实现真正的并行性 自然而然。

  • 内存安全掌握在您手中,而实现安全 当您忠实地遵守原则时,并不难 通过渠道发送的现在由所有 接收器。 换句话说,通过通道发送值 也是这些价值的所有权转移。

  • Go工具链具有集成的种族检测器,因此您 可以使用-race标志运行测试套件并创建评估 使用go build -race进行相同的程序构建 目的:当以这种方式检测到的程序运行时, 种族检测器一检测到它就会使它崩溃 不同步的读/写内存访问。 崩溃导致的打印输出包括 关于和哪里出错的解释性消息, 带有堆栈跟踪。

  • IPC速度很慢,因此增益很可能会被损失抵消。

所有方面,我认为没有真正的理由分开流程,除非 您正在编写类似电子邮件处理服务器的内容 这个概念很自然地出现。

答案 1 :(得分:1)

通道用于在goroutine之间进行通信,您不应该在类似以下代码的goroutine中使用它:

sensorData <- string(msg.Payload())
fmt.Println(<-sensorData) //currently not printing anything

如果要按通道测试打印,可以在同一goroutine中使用缓冲通道以避免阻塞,如下所示:

sensorData := make(chan []byte, 1)

欢呼