En la programación secuencial, constantemente encuentro el deseo obvio de no detener el programa en un momento en que el objetivo de ciertas tareas (procesos) son las acciones periódicas, por ejemplo, sondear los valores del sensor o transmitir datos en un horario a un servidor o la entrada / salida de una gran cantidad de datos. Lo más simple, por supuesto, es esperar la finalización del evento periódico y luego, lentamente, continuar realizando otras tareas.
while True: do_ext_proc_before() do_internal_proc() sleep(5) do_ext_proc_after()
Puede abandonar 'sleep ()' y habilitar la verificación de algunas condiciones en el ciclo, lo que le permitirá no retrasar el ciclo principal al menos hasta que ocurra un evento periódico:
start = time() set_timer(start,wait=5)
En la programación asincrónica, cada tarea se convierte en un proceso independiente y se ejecuta, dependiendo de la implementación específica, en paralelo o pseudo-paralelo, utilizando una comprensión interna de las condiciones de espera naturales o artificialmente establecidas o el uso de un recurso limitado, por ejemplo, un disco o un canal de comunicación.
setTask(do_ext_proc_before()) setTask(do_internal_proc(),timer=5,timeout=7,alarm_handler=alarm) setTask(do_ext_proc_after()) runTasks()
Ahora surge un problema que no existe en la programación secuencial: qué hacer si es necesario sincronizar algunos procesos con sus sistemas asíncronos.
haciendo? Por ejemplo, después de haber recibido datos de los sensores, inicie el proceso de enviar datos a un servidor o responda a una emergencia. Además, en la programación asincrónica, la organización de la entrada / salida asincrónica se resuelve orgánicamente en el lenguaje estándar, y otras situaciones se resuelven en las bibliotecas.
Estudié esta pregunta usando la biblioteca extendida asyncio Micropython publicada
Peter Hinch ( https://github.com/peterhinch/micropython-async/blob/master/TUTORIAL.md )
La solución más simple es señalar el evento a los procesos interesados. Para hacer esto, use la clase Event (), que contiene varios módulos
Event.Set( timeout = None, data = None ) - (Event = True), , , Event.IsSet() - , , True, False Event.Wait() - , - Done,Timeout,Cancel Event.Data() - , Event.Clear() - (Event = False).
La finalización se registra, como regla, por el proceso que está esperando que ocurra el evento, por ejemplo, el proceso de visualización en la pantalla o el proceso de guardar datos en el disco, o el tiempo de espera, entonces no hay necesidad de actualizar o guardar los datos, ya que no se actualizan por ningún motivo, o debido a su interrupción ante la ocurrencia de otro evento importante, por ejemplo, la transición al modo de suspensión o reinicio, que puede requerir la liberación de todos los procesos pendientes restableciendo los eventos correspondientes.
Debe tenerse en cuenta que es recomendable hacer Event.Clear () con un solo proceso, si esto no contradice el algoritmo dado. De lo contrario, si varios procesos están esperando que ocurra el evento Event.Set (), se supone que Event.Clear () debe ser realizado por uno de los procesos interesados, solo asegurándose de que todos los procesos interesados hayan respondido al evento. Esto complica la lógica de decisión cuando se usa Event-Class cuando se espera un evento por varios procesos. Esta situación se resuelve estableciendo una cierta cantidad de Clear () del evento que ocurrió.
Barrier.Set( quantity = 1, timeout = None, data = None ) - quantity = 1 Event.Set() Barrier.IsSet() - , , True, False Barrier.Wait() - , - Done,Timeout,Cancel Barrier.Data() - , Barrier.qty - Barrier.Clear() - (Event = False), Barrier.quantity , ,
Al mismo tiempo, no se mantiene ninguna contabilidad: qué proceso específico ya ha respondido y cuál aún no, lo que puede dar lugar al problema de volver a reaccionar ante el evento, si esto es esencial para un algoritmo dado. Si en lugar de Barrier.quantity pasa una lista de nombres de procesos interesados, se puede evitar este conflicto. Además, en caso de tiempo de espera o interrupción del evento, puede determinar qué procesos pendientes específicos aún no han funcionado. Todo lo anterior se aplica a una situación en la que uno o más procesos están esperando la ocurrencia de un determinado evento, o una situación de uno a muchos. Esto ocurre cuando el proceso o procesos do_ext_proc_after () durante la programación secuencial solo se ejecutarán después de completar do_internal_proc (). Para la conveniencia de una mayor comprensión, ampliaremos la clase de evento y la clase de barrera existentes en la nueva clase EEvent y la convertiremos en global o en los objetos generados por ella. Aquí 'creadores' es el nombre o la lista de nombres de procesos que desencadenan el evento o desbloquean el recurso, 'seguidores' es el nombre o la lista de nombres de procesos que esperan el evento o desbloquean el recurso
EEvent.Set (creators, folowers, timeout = None, data = None ) - True, EEvent.IsSet( procName ) - procName - ID EEvent.Wait( procName ) EEvent.Clear( procName ) EEvent.Folowers() - , . Barrier.qty = len(EEvent.List()) EEvent.Creators() - ,
Usando los módulos EEvent-Class, podemos describir la solución al problema discutido anteriormente.
def do_internal_proc(): ... EEvent.Set ('p_Creator',('p_Folwer1','p_Folwer2'))
Considere la situación opuesta: cuando un proceso está esperando la finalización de varios eventos, o una situación de "muchos a uno". En otras palabras, si la ejecución de do_internal_proc () puede ser solo después de la ejecución de do_ext_proc_before (). En el caso extremo, cuando un proceso está esperando la finalización / ocurrencia de un evento, la tarea puede resolverse usando la clase Event. Cuando se espera la finalización de varios eventos, por ejemplo, solo después de mostrar los datos recibidos y enviarlos al servidor, guardarlos en el disco, es necesario que cada proceso ejecutado establezca su participación en el evento esperado y espere hasta que se completen todos los procesos.
def do_ext_proc_before1() ... EEvent.Set('p_Creator1','p_Folwer') ... def do_ext_proc_before2() ... EEvent.Set('p_Creator2','p_Folwer') ... def do_internal_proc(): ... EEvent.Wait(('p_Creator1','p_Creator2')) ... EEvent.Clear('p_Folwer')
Otro aspecto importante de la programación asincrónica es compartir un recurso limitado. Por ejemplo, la actualización de datos debe llevarse a cabo mediante un solo proceso, el resto de los procesos que afirman una acción similar debe hacer cola o esperar hasta que los datos se actualicen. Al mismo tiempo, es posible que la lectura de datos para visualización o reenvío no sea crítica. Por lo tanto, es necesario conocer la lista de procesos competitivos al organizar eventos relevantes.
En el estándar de programación asíncrona, esta tarea se resuelve mediante módulos de clase de bloqueo. En el caso general, el problema también se puede resolver de manera similar a la situación de uno a muchos.
def do_internal_proc():
Además de las opciones consideradas, existen soluciones que limitan el rendimiento, organizan las colas y la programación controlada de los procesos, pero en mi actividad aún no ha sido necesario y, como resultado, la necesidad de una comprensión suficiente para mí, aunque no excluyo que haya más elegante o decisiones económicas
En conclusión, quiero decir que los enfoques secuenciales y asincrónicos tienen el mismo derecho a existir e implementar con éxito los algoritmos dados. Por lo tanto, la aplicación de este o aquel enfoque está determinada por las prioridades del creador, que es más importante para él cuando implementa los algoritmos dados, transparencia y legibilidad, velocidad o volumen del código resultante.