我是asyncio的新手(与python3.4一起使用),我不确定我是否应该使用它。我在this thread中看到它可以用来每n秒执行一次函数(在我的情况下为ms),而不必深入研究线程。
我每隔n ms使用它通过基本串行协议从激光传感器获取数据,直到我得到m个样本。
以下是我的功能定义:
def countDown( self,
loop,
funcToDo,
*args,
counter = [ 1 ],
**kwargs ):
""" At every call, it executes funcToDo ( pass it args and kwargs )
and count down from counter to 0. Then, it stop loop """
if counter[ 0 ] == 0:
loop.stop()
else:
funcToDo( *args, **kwargs )
counter[ 0 ] -= 1
def _frangeGen( self, start = 0, stop = None, step = 1 ):
""" use to generate a time frange from start to stop by step step """
while stop is None or start < stop:
yield start
start += step
def callEvery( self,
loop,
interval,
funcToCall,
*args,
now = True,
**kwargs ):
""" repeat funcToCall every interval sec in loop object """
nb = kwargs.get( 'counter', [ 1000 ] )
def repeat( now = True,
times = self._frangeGen( start = loop.time(),
stop=loop.time()+nb[0]*interval,
step = interval ) ):
if now:
funcToCall( *args, **kwargs )
loop.call_at( next( times ), repeat )
repeat( now = now )
这就是我使用它的方式(getAllData是管理串行通信的功能):
ts = 0.01
nbOfSamples = 1000
loop = asyncio.get_event_loop()
callEvery( loop, ts, countDown, loop, getAllData, counter = [nbOfSamples] )
loop.run_forever()
我想把这个bloc放到一个函数中并按照我想要的方式调用它,就像这样:
for i in range( nbOfMeasures ):
myFunction()
processData()
但第二次测试不会调用getAllData 1000次,只调用两次,有时是三次。有趣的事实是,有两次我得到的数据和我想要的一样多。我不太懂,我在文档中找不到任何内容,所以我请求你的帮助。欢迎任何解释或更简单的方法:)
答案 0 :(得分:1)
你的事情太复杂了,一般来说,当你有一个事件循环时做递归就是糟糕的设计。
只有在您使用协同程序时,import asyncio as aio
def get_laser_data():
"""
get data from the laser using blocking IO
"""
...
@aio.coroutine
def get_samples(loop, m, n):
"""
loop = asyncio event loop
m = number of samples
n = time between samples
"""
samples = []
while len(samples) < m:
sample = yield from loop.run_in_executor(None, get_laser_data)
samples.append(sample)
yield from aio.sleep(n)
return samples
@aio.coroutine
def main(loop):
for i in range(nbOfMeasures):
samples = yield from get_samples(loop, 1000, 0.01)
...
loop = aio.get_event_loop()
loop.run_until_complete(main(loop))
loop.close()
才是有趣。这是一种方法:
asyncio
如果您对此感到困惑,请考虑阅读有关aio.sleep
的一些教程/文档。
但我想指出,必须使用线程从激光传感器获取数据。在运行事件循环的同一线程中执行任何阻塞IO将阻止循环并抛弃yield from loop.run_in_executor(None, get_laser_data)
。这就是get_laser_data
正在做的事情。它在一个单独的线程中运行recursive=False
函数。
答案 1 :(得分:0)
在python 3.5中,您可以使用async for
语法并创建asynchronous iterator来控制时间范围。它必须实现__aiter__
和__anext__
方法:
class timeframes(collections.AsyncIterator):
def __init__(self, steps, delay=1.0, *, loop=None):
self.loop = asyncio.get_event_loop() if loop is None else loop
self.ref = self.loop.time()
self.delay = delay
self.steps = steps
self.iter = iter(range(steps))
async def __anext__(self):
try:
when = self.ref + next(self.iter) * self.delay
except StopIteration:
raise StopAsyncIteration
else:
future = asyncio.Future()
self.loop.call_at(when, future.set_result, None)
await future
return self.loop.time()
async def __aiter__(self):
return self
这是一个模拟执行的协程:
async def simulate(steps, delay, execution):
# Prepare timing
start = loop.time()
expected = steps * delay - delay + execution
# Run simulation
async for t in timeframes(steps, delay):
await loop.run_in_executor(None, time.sleep, execution)
# Return error
result = loop.time() - start
return result - expected
这就是你在linux操作系统上得到的结果:
>>> loop = asyncio.get_event_loop()
>>> simulation = simulate(steps=1000, delay=0.020, execution=0.014)
>>> error = loop.run_until_complete(simulation)
>>> print("Overall error = {:.3f} ms".format(error * 1000))
Overall error = 1.199 ms
在Windows操作系统上有所不同(见this answer),但事件循环将赶上,整体错误不应超过15毫秒。