並行和事件驅動基本上是正交的,儘管並行化事件通常很「容易」。我將首先介紹事件驅動,然後是並行化,儘管您可能只想使用後者。
python中的「正常」控制流是迭代的。 這意味着你定義了代碼應該執行的指令,然後PC執行這些步驟來執行步驟。 有不同的方式來組織你的代碼(功能,事件驅動,面向對象,雖然我不想說這些是絕對的類別,你只能做X或Y)。事件驅動通常意味着您定義事件以及如何處理事件。 沒有什麼事情可以用事件驅動程序進行編程,而無法對迭代程序進行編程,反之亦然。
當引入asyncio庫時,Python主要獲得了對版本3.4的asyncronuos東西的支持。 有了3.5,你還得到了語法糖await
和async
。因爲你在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爲您提供的功能之一。
您如何閱讀RFID信息?是否只有在有信息時才能閱讀的文件描述符? – syntonym
https://tutorials-raspberrypi.de/raspberry-pi-rfid-rc522-tueroeffner-nfc/這是我使用的模塊,對不起它的德文。整個項目是在樹莓派上完成的 – Bajt
你是哪個Python版本的? – syntonym