简体   繁体   English

Python中“收集并执行”的最佳实践

[英]Best practice to “collect and execute in a bunch” in Python

I just fund myself implementing a timer-based version of "handle a list of events as a bunch" in order to safe resources - again - and I'm wondering whether there is a nice common pythonic approach.我只是资助自己实现一个基于计时器的“将事件列表作为一组处理”的版本,以保护资源 - 再次 - 我想知道是否有一个很好的通用 pythonic 方法。

You probably know this: you're handling recurring events like mouse movements, file system changes etc. and you have to do some calculation as a reaction to those events but it would be great if you could use a little break in the stream of events to handle them in a bunch.您可能知道这一点:您正在处理重复发生的事件,例如鼠标移动、文件系统更改等,并且您必须进行一些计算作为对这些事件的反应,但如果您可以在 stream 事件中使用一点中断,那就太好了成堆地处理它们。 Maybe because older events get invalidated by newer events (and it's enough to handle the oldest ones) or because events can somehow be squashed together.可能是因为较旧的事件被较新的事件无效(并且足以处理最旧的事件),或者因为事件可以以某种方式被挤压在一起。

Examples are: mouse movements (draw only latest position), "auto save" in editors or auto-sync on file systems, or (in my example) monitoring file system changes and re-compile something.示例包括:鼠标移动(仅绘制最新位置)、编辑器中的“自动保存”或文件系统上的自动同步,或(在我的示例中)监视文件系统更改并重新编译某些内容。

Usually I look up how to use a Timer and think about how I could avoid an extra thread and come up with some semi-finished but complex solution for a - in my eyes - very simple problem.通常我会查看如何使用Timer并思考如何避免额外的线程,并为一个在我看来非常简单的问题提出一些半成品但复杂的解决方案。 Lot of questions arise:出现很多问题:

  • how to avoid concurrent handling (eg if I use a threading.Timer and start a thread doing the work)如何避免并发处理(例如,如果我使用threading.Timer并启动一个线程来完成工作)
  • how to make sure there is a time limit for events to be handled (in case of a continuous incoming of events without break)如何确保处理事件有时间限制(如果事件连续传入而没有中断)
  • how to avoid threads if possible如果可能,如何避免线程
  • how to avoid creating an overly-complex framework如何避免创建过于复杂的框架
  • (you name it) (你的名字)

What I'd like to have is something which works like this:我想要的是这样工作的东西:

timer = SomeContinuousTimer()
new_events = []
while True:
   event = wait_for(inotify_adapter.event_gen(), timer.timeout())
   if event == timer.TIMEOUT:
       my_handler_func(new_events)
   else:
       new_events.append(event)
       timer.restart(1500)

But wait_for would have to act like select and for this I'd need file descriptors and the above code is already a bit more than I would actually expect it to be.但是wait_for必须像select那样行事,为此我需要文件描述符,上面的代码已经比我实际预期的要多一些。

What I would be really glad about to have would be used like this:我真的很高兴会像这样使用:

bunch_handler = BunchHandler()
new_events = []

def read_events():
    for event in inotify_adapter.event_gen():
        new_events.append(event)

while True:
    # will run `read_events` asynchronously until 1.5sec have passed since the
    # last event
    bunch_handler.read(read_fn=read_events, bunch_wait=1500)

    handle_events(new_events)

Is this a typical scenario I should use async / await for?这是我应该使用async / await的典型场景吗? Are there frameworks for the case where async is not an option?对于不能选择async的情况,是否有框架? Is there an async framework for this exact scenario?是否有适用于这种确切场景的异步框架?

This is not nice but it does what I want and might act as an example which shows, what I'm talking about:)这不是很好,但它做了我想要的,并且可以作为一个例子来说明我在说什么:)

import asyncio
import time

async def event_collector(*, listener_fn, bunch_wait=1.0, max_wait=2.0):
    """Wait for (but don't handle) events and wait for a maximum of @bunch_wait seconds after the
    last event before returning. Force return after @max_wait seconds"""
    max_time_task = asyncio.Task(asyncio.sleep(max_wait))
    while True:
        resetable = asyncio.Task(asyncio.sleep(bunch_wait))
        done, _ = await asyncio.wait(
            {listener_fn.__anext__(), resetable, max_time_task},
            return_when=asyncio.FIRST_COMPLETED)
        if resetable in done or max_time_task in done:
            return
        resetable.cancel()


async def generate_events(events):
    """Simulates bursts of events with side-effects"""
    while True:
        for i in range(5):
            await asyncio.sleep(.01)
            events.append(i)
            print("*" * len(events))
            yield
        await asyncio.sleep(3.200)


def handle_events(events):
    """Simulates an event handler operating on a given structure"""
    print("Handle %d events" % len(events))
    events.clear()


async def main():
    new_events = []
    t = time.time()
    while True:
        await event_collector(listener_fn=generate_events(new_events), bunch_wait=1.1, max_wait=2.2)

        now = time.time()
        print("%.2f" % (now - t))
        t = now
        handle_events(new_events)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

This approach has some shortcomings: * you need to listen for events asynchronously using async * event_collector will return after max_wait seconds regardless whether any events have been seen yet (so it acts like a timeout if no events occur) * instead of resetting a timer, a new one gets created every time这种方法有一些缺点: * 您需要使用async侦听事件 * event_collector 将在max_wait秒后返回,无论是否已经看到任何事件(因此如果没有事件发生,它就像超时) * 而不是重置计时器,每次都会创建一个新的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM