2017-12-03 185 views
0

我对Python相当陌生,所以也许我应该如何工作的整个概念是错误的:Python中的并行或事件驱动函数?

我正在为时间管理目的构建RFID阅读器。例如。用户使用RFID芯片登录 - >计时器开始每隔一分钟计算并更新Google电子表格。更新部分工作正常,但需要一段时间。但我想一直检查RFID登录。我读过的地方就是我正在寻找的事件驱动编程。

目前我正在做一切在一段时间的真正的循环,这感觉就像一个黑客本身。我能以某种方式在RFID阅读器发送信号时执行我的代码吗?然后,我的更新每分钟左右运行一次?我想知道这里的最佳做法。

+0

您如何阅读RFID信息?是否只有在有信息时才能阅读的文件描述符? – syntonym

+0

https://tutorials-raspberrypi.de/raspberry-pi-rfid-rc522-tueroeffner-nfc/这是我使用的模块,对不起它的德文。整个项目是在树莓派上完成的 – Bajt

+0

你是哪个Python版本的? – syntonym

回答

1

并行和事件驱动基本上是正交的,尽管并行化事件通常很“容易”。我将首先介绍事件驱动,然后是并行化,尽管您可能只想使用后者。

python中的“正常”控制流是迭代的。 这意味着你定义了代码应该执行的指令,然后PC执行这些步骤来执行步骤。 有不同的方式来组织你的代码(功能,事件驱动,面向对象,虽然我不想说这些是绝对的类别,你只能做X或Y)。事件驱动通常意味着您定义事件以及如何处理事件。 没有什么事情可以用事件驱动程序进行编程,而无法对迭代程序进行编程,反之亦然。

当引入asyncio库时,Python主要获得了对版本3.4的asyncronuos东西的支持。 有了3.5,你还得到了语法糖awaitasync。因为你在2.7这不适合你。 asyncio有一个backport,命名为trollius,但如果你只有“少量的事件”,这是矫枉过正。此外,“滚动你自己的基本事件循环”并不难(当然asyncio和trollius做得更多,但如果我们不打算使用这些功能,为什么要这么做?)。

的基本工作流程正在等待事件,然后处理他们,因为他们发生:

events = [] 

while waiting_for_events: 
    if events: 
     event = events.pop() 
     handle_event(event) 

你会莫名其妙地需要知道如何对事件以及如何处理它们之间的区别。 对于“全功能事件循环”,您可能会使用具有继承的不同类,但只需使用每个事件的名称即可。 另外,我们可能需要某种data,比如我们遇到的RFID。

from collections import namedtuple 
Event = namedtuple("Event", ["name", "data"]) 

然后,我们只需要映射事件如何处理它们:

def handle_rfid(data): 
    ... 

def handle_timer(data): 
    ... 

event_handler_mapping = {"rfid": handle_rfid, "timer": handle_timer} 

def handle_event(event): 
    event_handler_mapping[event.name](event.data) 

我们仍然需要产生事件,所以让我们改写事件循环,以获取事件:

timer = 0 
current_time = time.perf_counter() 

while waiting_for_events: 

    rfid = get_rfids() 
    if rfid: 
     events.append(Event("rfid", rfid)) 

    if timer > 1: 
     events.append(Event("timer", timer)) 
     timer = 0 
    else: 
     timer += time.perf_counter() - current_time 
     current_time = time.perf_counter() 

    if events: 
     event = events.pop() 
     handle_event(event) 

现在我们是“事件驱动”。好的是,我们可以轻松地将其扩展到更多的事件。 不好的一点是,它仍然会做同样的事情,你可能已经有了,但它更复杂。 此外,如果事件处理需要大量时间(这似乎是更新电子表格的情况),其他事件将不会生成和处理 。这是并行性发挥作用。

并行性基本上意味着我们可以使用多个内核。 在这里,我们实际上只需要“并发”,这意味着两件事情可以一次发生。 这比真正的并行性更“容易”,我们可以在不同的事物之间切换,但仍然按顺序完成所有事情。 在Python中,这基本上归结为多处理(并行)和线程(“唯一”并发)(在其他编程语言线程实际上并行,但在Python中,这是因为我不想进入情况)。 并发问题始终是同步。如果事情可能同时发生,如果两个线程尝试更改相同的变量,则可能发生坏事 。一般来说,只要你使用线程安全函数来访问线程之间共享的变量,你就很安全。

python线程由threading模块创建。 如果您不知道其他地方的线程,我个人觉得很难理解,但要点如下: 要在线程中运行函数,请使用threading.Thread(target=function),然后使用thread.start()。 你可以使用它执行以下操作:

def run_in_thread(f, *args, **kwargs): 
    thread = Thread(target=f, args=args, kwargs=kwargs) 
    thread.start() 

def _update_spreadsheet(data): 
    # logic here 

# when using the event driven approach from above 
def handle_timer(data): 
    run_in_thread(_update_spreadsheet(data)) 

请注意,如果您访问从_update_spreadsheet内的变量,你必须carefule只使用线程安全功能。 尽可能少使用线程间通信是“最佳”。 A queue通常是一个不错的选择。

您可以在没有事件驱动的组织的情况下使用并行/并发。 因为我们已经将代码分成事件处理程序,所以我们可以在单独的线程中调用长时间运行的事件处理程序。

如果我们有很多事件和事件处理程序在线程中运行一切是一个坏主意(因为线程切换有开销)。 因此,asyncio(以及可能所有其他事件循环)实现某种“等待,直到至少一个事件可以处理”。 这是互联网输入和输出最有趣的,因为这些需要“很长时间”。 通常使用类似select的东西。其他事件(定时器,从磁盘读取数据,等待某些硬件事件,......)需要其他机制来“在发生事情时唤醒我”。整合所有这些是asyncio为您提供的功能之一。

+0

哇,非常感谢你的详细解答!我目前正在工作,本周晚些时候会检查出来,但它为我解决了很多问题。 – Bajt