2016-11-07 29 views
0

我想寫一些asyncio代碼,持有多個用戶/連接的會話狀態。保持會話狀態與發電機,而不是FSM在asyncio

這裏是一個小玩具例子:

Please tell me your name: 
asd 
how old are you? 
40 
asd is 40 years old 

我想實現這一點的最好的辦法是像發生器功能:

class MessageReciever(): 
    async def start_conversation(user_id): 
     await send_message(user_id, "Please tell me your name") 
     name = await wait_for_reply(user_id) 
     await send_message(user_id, "how old are you?") 
     age = await wait_for_reply(user_id) 
     await send_message(user_id, "{} is {} years old".format(name, age) 

目前,我只有一個功能async on_message(user_id, message)是獲得每次收到消息時都會調用它。對於我現在擁有無國籍的所有東西來說,這很好。

這個理想的做法提出了許多問題:

  • 我不知道如何等待()是由我自己的代碼觸發的事件。
  • 如果時間過長,我不知道如何超時。我不想將會話永久保存在RAM中,似乎並沒有簡單的方法來結束正在運行的協程或將其清除到數據庫。

解決此問題的典型方法是編寫FSM,並將每個用戶的狀態保存在單獨的結構中,但編寫和調試大型FSM非常煩人,尤其是在原型設計時。我在FSM中也不需要任何循環。

我該如何實現一種簡單的方法來保持asyncio中有狀態的「對話」?

+0

你對這個問題的答案仍然感興趣嗎? – Omnifarious

+0

當然,我現在寫了一些非常難看的代碼。 – Atsch

回答

2

處理此問題的最佳方法是停止考慮事件和回調,並考慮讓線程代替。除了與普通線程不同,它可以隨時中斷程序以切換到其他線程,只有當您使用await來調用某些線程時,纔會中斷線程。

在這個模型中,你會傾向於在堆棧上保留很多本地狀態,除非你有狀態你需要在線程之間共享。然後當你撥打await電話時,你應該確保它沒有處於不一致的狀態。

如果您需要啓動一個新線程,請針對您正在使用的庫/框架使用適當的調用。對於asyncio,那將是asyncio.ensure_future(這不是一個好名字)。如果您需要在線程之間進行通信,請使用線程間(以及這些線程之間的奇怪線程)隊列。對於asyncio這將是一個asyncio.Queue。我建議你總是指定一個最大隊列大小,以避免某個線程在其他線程從隊列中讀取它之前將大量內容發送到另一個線程的情況。

間'thread」隊列類將有一個await能夠putget方法使線程可以暫停,如果隊列已滿,等待它發送更多的前清空位。

如果您以這種方式思考問題,編寫程序的正確方法將自然發生在您身上。

要獲得關於如何伺候自己的事件之一,只需使用一個跨'thread」隊列和您的活動發佈到這一點,必須將等待它從隊列中讀取的東西的問題。

asyncio模塊也有一種方法來超時await。這樣做:

await asyncio.wait_for(thing_to_timeout,10)#等10秒鐘後返回無論。

如果在thing_to_timeout返回答案前時間到期,它將拋出asyncio.TimeoutError