如何 运行 一个 zmq 和另一个 while True: 同时独立?

How to run a zmq and other while True: at the same time independently?

我的服务器有这个代码:

import time
import zmq

context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5000")

while True:
    message = socket.recv()
    socket.send(b"World")
    print "sent"

while True:
    print "done."

我有一个单独的客户端脚本,每当我发送一条消息时,它都会通过 zmq 向这个脚本发送一条消息。在服务器上(这段代码),如果我只有第一个 while True:,它会在我每次发送消息时打印 "sent",如果我只有第二个 while True:,它会打印 "done."不断地。但是,如果我同时放置两者,它永远不会打印完成(或者如果我切换他们的顺序并同时放置它们,当我发送消息时它永远不会打印 "sent"”)。

作为输出,我希望它连续打印 "done.",并在收到消息时打印 "sent"。所以像这样:

done.
done.
done.
done.
done.
sent
done.
lots more done....

基本上我希望两个循环 运行 连续且完全独立于彼此。

N.B。我曾尝试使用多处理(例如此处的第 3 个答案 How do I run two python loops concurrently?),但也无法使其正常工作。我试过如下:

import time
import zmq
from multiprocessing import Process

context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5000")
i = time.time()

def zmq_loop():
    while True:
        message = socket.recv()
        socket.send(b"World")
        print "sent"

def done_loop():
    global i
    while True:
        i2 = time.time()-i
        if i2%2 == 0:
            print "done."

if __name__ == "__main__":
    Process(target=zmq_loop).start()
    Process(target=done_loop).start()

正如昨天在 中解释的那样,[CONCURRENT]-处理在 python 中可以通过多种不同的方式在技术上实现,每种方式的成本都不同。

今天,让我们来看看另一种方法 - 使用一个框架,它的开发动机完全相同 - 具有自然的 [CONCURRENT]-已经在 DNA 中的调度 - 最初旨在轻松编写和流畅操作复杂的 GUI 人机交互 (MMI)。

这个框架可能而且将会帮助你实现很多,因为事实上,它已经在完全相同的场景中得到了很多关注,在这些场景中,必须同时监控不止一件事:

欢迎使用 Tkinter GUI 框架,我们仅将其用于智能并发操作的事件处理程序。

我多次感到非常惊讶,构建一个相当复杂的有限状态自动机 (FSA) 组合是多么容易,它可以顺利合作(FSA-s 的联盟),使用独立的工具,隔离操作(每个 FSA 的内部逻辑),但能够轻松地将信号/消息从一个 FSA 传播到另一个 FSA。是的,他们实际上可以在 1-event-source-FSA : N-consumer(s)-FSA(s)

在那里你可以创建(使用 ZeroMQ 始终以非阻塞方式)处理程序——一个 "sniffer"定期检查(最好通过超时控制的 .poll() 方法来 { NACK | POSACK } 任何要读取的内容)——另一个 "reader" 用于从 ZeroMQ Socket() 实例实际读取(由来自 "sniffer" 的 POSACK 信号触发,如前所述 - 另一个 "do-a-work-er" 对于任何其他可能希望操作的任务

Tkinter .mainloop() 方法是全局控制器,它为您编排肮脏的工作。

Tkinter 介导的代理协同处理的高级概念从 main() 开始,非常简单:

def main():
    root = Tk()                      # INIT a Tk() instance
    root.lift()                      #      + make it visible
    app = myApplication( root )      # SET ( [HERE] are all your app gems )
    root.mainloop()                  # START the core event-handling orchestrator

Tkinter 可能看起来像一个充满各种 GUI 小工具的车库,与您的问题无关,但不要惊慌。

Tkinter 拥有非常适合您需求的出色工具。

  • using control variables 将用作在其他独立且明确未协调的参与者之间存储、发信号和传播值变化的手段(参考文献 "sniffer","reader"、"worker" 和任何其他...)

  • tools for handling events - real, abstract and even virtual

  • 用于处理定时操作的工具 - 几乎是轻量级实时系统的形式,使用设置了接下来会发生什么的首选时间, .mainloop()-尚需牢记
    明确指定的时间 .after( thisAmountOfMILLISECONDS, callThisFUNCTION ) 或自由 .after_idle( callAlwaysThatFUNCTION ).

使用这些已经很完美的工具,您确实不需要任何其他东西来解决您的任务。

所以剩下的只是原则上在你的创造力下如何重新使用这些智能工具。


一个小演示,
如何使两(3!)件事发生"at the same time independently"

让我们设置案例,当一个人想要同时处理(这里通过打印演示)几个独立的进程时。

    >>> #-----------------------------------------------FAST MOCK-UP EXAMPLE
    >>> import Tkinter as tk                          # python27
    >>> root = tk.Tk()
    >>> root.protocol( "WM_DELETE_WINDOW", root.quit() )
    '3071841620Ldestroy'
    >>> #------VAR-------------------------------------IMPORTANT TOOL:
    >>> aStringVAR = tk.StringVar()
    >>> aStringVAR.set( "_init_" )

    >>> def aKeyPressEventHANDLER( anEvent ): # SIMPLE EventHANDLER,
            #                                 #        also ignites remote responsive processes
    ...     aTemplate = "[KEY]::{3: >10s}\n<s/n>::{0: >10d}\n(=@=)::{1: > 10d}\n^from::({5:})"
    ...     sString   = aTemplate.format( anEvent.serial,
    ...                                   anEvent.time,
    ...                                   anEvent.char,
    ...                                   anEvent.keysym,
    ...                                   anEvent.keysym_num,
    ...                               str(anEvent.widget )
    ...                               )
    ...     aStringVAR.set( sString )
    ...     print sString
    ... 
    >>> #----VAR_TRACER----------------------------------------[#1]
    >>> def aVAR_TRACER_A( p1_quasiNAME, p2_indexOrEmptyString, p3_accessMODE ):
    ...     print "aVAR_TRACER_A()-called::(on){0:} traced_event({1:})".format( str( p1_quasiNAME ), str( p3_accessMODE ) )
    ...     # ###############=[A]#######
    ...     # < do some task =[A] here >
    ...     # ###############=[A]#######
    ...     print "aVAR_TRACER_A()         [{0:}]".format(   str( root.globalgetvar( p1_quasiNAME ) ).replace( " ", "" ) )
    ...

    >>> #----VAR_TRACER----------------------------------------[#2]
    >>> def aVAR_TRACER_B( p1_quasiNAME, p2_indexOrEmptyString, p3_accessMODE ):
    ...     print "aVAR_TRACER_B()-called::(on){0:} traced_event({1:})".format( str( p1_quasiNAME ), str( p3_accessMODE ) )
    ...     # ###############=[B]#######
    ...     # < do some task =[B] here >
    ...     # ###############=[B]######
    ...     print "aVAR_TRACER_B()         [{0:}]".format(   str( root.globalgetvar( p1_quasiNAME ) ).replace( " ", "" ) )
    ... 

    >>> #-----VAR_A_tracer_ID------------------------------"w" EVENT SNIFFER
    >>> aTraceVAR_A_tracer_ID = aStringVAR.trace_variable( "w", aVAR_TRACER_A )

    >>> #-----VAR_B_tracer_ID------------------------------"w" EVENT SNIFFER
    >>> aTraceVAR_B_tracer_ID = aStringVAR.trace_variable( "w", aVAR_TRACER_B )

    >>> #-----------tracer_ID values for ev. theirs resp. de-activation:
    >>> aTraceVAR_A_tracer_ID
    '3071960124LaVAR_TRACER_A'
    >>> aTraceVAR_B_tracer_ID
    '3071961284LaVAR_TRACER_B'

    >>> #---.bind()-----------------------EventHANDLER with a system event <KeyPress>
    >>> root.bind( "<KeyPress>", aKeyPressEventHANDLER ) # <-since here LIVE (!)
    '3071841740LaKeyPressEventHANDLER'
    >>> #------------------------------------------------^^^ since here, it went live
    >>> #                                                 1: having put a mouse on tk-window,
    >>> #                                                 2: set-focus by click
    >>> #                                                 3: started keys:
    >>> #                                                    ( "a",
    >>> #                                                       6-on-<NumKeyPad>,
    >>> #                                                       *-on-<NumKeyPad>

    >>> # this happened "independently, at the same time" ( see time (=@=):: values )
    >>> 

    aVAR_TRACER_B()-called::(on)PY_VAR0 traced_event(w)
    aVAR_TRACER_B()         [[KEY]::a<s/n>::832(=@=)::88486992^from::(.)]
    aVAR_TRACER_A()-called::(on)PY_VAR0 traced_event(w)
    aVAR_TRACER_A()         [[KEY]::a<s/n>::832(=@=)::88486992^from::(.)]
    [KEY]::         a
    <s/n>::       832
    (=@=)::  88486992
    ^from::(.)
    aVAR_TRACER_B()-called::(on)PY_VAR0 traced_event(w)
    aVAR_TRACER_B()         [[KEY]::KP_6<s/n>::832(=@=)::88509107^from::(.)]
    aVAR_TRACER_A()-called::(on)PY_VAR0 traced_event(w)
    aVAR_TRACER_A()         [[KEY]::KP_6<s/n>::832(=@=)::88509107^from::(.)]
    [KEY]::      KP_6
    <s/n>::       832
    (=@=)::  88509107
    ^from::(.)
    aVAR_TRACER_B()-called::(on)PY_VAR0 traced_event(w)
    aVAR_TRACER_B()         [[KEY]::KP_Multiply<s/n>::832(=@=)::88541180^from::(.)]
    aVAR_TRACER_A()-called::(on)PY_VAR0 traced_event(w)
    aVAR_TRACER_A()         [[KEY]::KP_Multiply<s/n>::832(=@=)::88541180^from::(.)]
    [KEY]::KP_Multiply
    <s/n>::       832
    (=@=)::  88541180
    ^from::(.)