我正在开发一个带有Raspberry Pi 3的项目,用于环境控制,在连续循环中有许多简单的重复事件。 RP3对于这项工作来说是过于资格的,但它让我专注于其他事情。
申请特征:
我熟悉(不是专业)VB.NET,但决定在Python 3.6中完成这个项目。 最近几个月我读了很多关于设计模式,线程,流程,事件,并行处理等主题的文章。
根据我的阅读,我认为Asyncio与Executor中的一些任务相结合可以完成这项任务。
大多数任务/事件不是时间关键的。控制器输出可以使用'最新'的sensordata。 另一方面,一些任务在一段时间内激活继电器。我想知道如何编程这些任务,而没有机会另一个“耗时”的任务是阻止处理器在一段时间内(例如)CO2阀打开。这对我的环境来说可能是灾难性的。
因此我需要一些建议。
到目前为止,请参阅下面的代码。我不确定我是否正确使用了Python中的Asyncio函数。 为了便于阅读,我将把各种任务的内容存储在不同的模块中。
import asyncio
import concurrent.futures
import datetime
import time
import random
import math
# define a task...
async def firstTask():
while True:
await asyncio.sleep(1)
print("First task executed")
# define another task...
async def secondTask():
while True:
await asyncio.sleep(5)
print("Second Worker Executed")
# define/simulate heavy CPU-bound task
def heavy_load():
while True:
print('Heavy_load started')
i = 0
for i in range(50000000):
f = math.sqrt(i)*math.sqrt(i)
print('Heavy_load finished')
time.sleep(4)
def main():
# Create a process pool (for CPU bound tasks).
processpool = concurrent.futures.ProcessPoolExecutor()
# Create a thread pool (for I/O bound tasks).
threadpool = concurrent.futures.ThreadPoolExecutor()
loop = asyncio.get_event_loop()
try:
# Add all tasks. (Correct use?)
asyncio.ensure_future(firstTask())
asyncio.ensure_future(secondTask())
loop.run_in_executor(processpool, heavy_load)
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print("Loop will be ended")
loop.close()
if __name__ == '__main__':
main()
答案 0 :(得分:1)
大多数任务/事件不是时间关键的。控制器输出可以使用'最新'的sensordata。另一方面,一些任务在一段时间内激活继电器。我想知道如何编程这些任务,而没有机会另一个“耗时”的任务是阻止处理器在一段时间内(例如)CO2阀打开。这对我的环境来说可能是灾难性的。
请允许我强调Python 不是实时语言,而asyncio不是实时组件。它们既不运行实时执行的基础设施(Python是垃圾收集的,通常在分时系统上运行),也没有在实践中在这样的环境中进行过测试。因此,我强烈建议不要在任何情况下使用它们,因为错误可能会对您的环境造成灾难。
有了这个,你的代码就会出现问题:虽然heavy_load
计算不会阻止事件循环,但它永远不会完成,也不会提供有关其进度的信息。 run_in_executor
背后的想法是,您正在运行的计算最终将停止,并且事件循环将希望得到有关它的通知。 run_in_executor
的惯用法可能如下所示:
def do_heavy_calc(param):
print('Heavy_load started')
f = 0
for i in range(50000000):
f += math.sqrt(i)*math.sqrt(i)
return f
def heavy_calc(param):
loop = asyncio.get_event_loop()
return loop.run_in_executor(processpool, do_heavy_calc)
表达式heavy_calc(...)
不仅在不阻塞事件循环的情况下运行,而且还等待。这意味着异步代码可以等待其结果,也不会阻止其他协同程序:
async def sum_params(p1, p2):
s1 = await heavy_calc(p1)
s2 = await heavy_calc(p2)
return s1 + s2
上面一个接一个地运行两个计算。它也可以并行完成:
async def sum_params_parallel(p1, p2):
s1, s2 = await asyncio.gather(heavy_calc(p1), heavy_calc(p2))
return s1 + s2
可以改进的另一件事是设置代码:
asyncio.ensure_future(firstTask())
asyncio.ensure_future(secondTask())
loop.run_in_executor(processpool, heavy_load)
loop.run_forever()
调用asyncio.ensure_future
然后从不等待结果有点像asyncio反模式。无拘无束的任务引发的例外情况被默默地吞噬,这几乎肯定不是你想要的。有时候人们只是忘记写await
,这就是为什么asyncio会在循环被破坏时抱怨未被挂起的待处理任务的原因。
安排某人等待每项任务的良好编码习惯,可以立即使用await
或gather
将其与其他任务相结合,或者在以后点。例如,如果任务需要在后台运行,您可以将其存储在某处await
或在应用程序生命周期结束时取消它。在您的情况下,我会将gather
与loop.run_until_complete
合并:
everything = asyncio.gather(firstTask(), secondTask(),
loop.run_in_executor(processpool, heavy_load))
loop.run_until_complete(everything)
答案 1 :(得分:0)
某些事件属于"安全"并且应该在触发时立即运行(故障安全传感器,紧急按钮)。
然后我强烈建议您不要依赖软件来实现此功能。切断电源的紧急停止按钮是通常完成的事情。如果你有软件这样做,并且你真的有一个面临生命危险的处境,你就会陷入一堆祸患中 - 几乎可以肯定你需要遵守大量的法规。遵守。