@@ -650,49 +650,37 @@ Synchronous Methods:
650
650
Asynchronous Method:
651
651
* ` wait ` Pause until event is set.
652
652
653
- Coros waiting on the event issue ` await event.wait() ` when execution pauses until
654
- another issues ` event.set() ` .
655
-
656
- This presents a problem if ` event.set() ` is issued in a looping construct; the
657
- code must wait until the event has been accessed by all waiting tasks before
658
- setting it again. In the case where a single task is awaiting the event this
659
- can be achieved by the receiving task clearing the event:
660
-
661
- ``` python
662
- async def eventwait (event ):
663
- await event.wait()
664
- # Process the data
665
- event.clear() # Tell the caller it's ready for more
666
- ```
667
-
668
- The task raising the event checks that it has been serviced:
669
-
670
- ``` python
671
- async def foo (event ):
672
- while True :
673
- # Acquire data from somewhere
674
- while event.is_set():
675
- await asyncio.sleep(1 ) # Wait for task to be ready
676
- # Data is available to the task, so alert it:
677
- event.set()
678
- ```
679
-
680
- Where multiple tasks wait on a single event synchronisation can be achieved by
681
- means of an acknowledge event. Each task needs a separate event.
682
-
683
- ``` python
684
- async def eventwait (event , ack_event ):
685
- await event.wait()
686
- ack_event.set()
687
- ```
688
-
689
- This is cumbersome. In most cases - even those with a single waiting task - the
690
- Barrier class offers a simpler approach.
653
+ Tasks wait on the event by issuing ` await event.wait() ` ; execution pauses until
654
+ another issues ` event.set() ` . This causes all tasks waiting on the ` Event ` to
655
+ be queued for execution. Note that the synchronous sequence
656
+ ``` python
657
+ event.set()
658
+ event.clear()
659
+ ```
660
+ will cause waiting task(s) to resume in round-robin order.
661
+
662
+ The ` Event ` class is an efficient and effective way to synchronise tasks, but
663
+ firmware applications often have multiple tasks running ` while True: ` loops.
664
+ The number of ` Event ` instances required to synchronise these can multiply.
665
+ Consider the case of one producer task feeding N consumers. The producer sets
666
+ an ` Event ` to tell the consumer that data is ready; it then needs to wait until
667
+ all consumers have completed before triggering them again. Consider these
668
+ approaches:
669
+ 1 . Each consumer sets an ` Event ` on completion. Producer waits until all
670
+ ` Event ` s are set before clearing them and setting its own ` Event ` .
671
+ 2 . Consumers do not loop, running to completion. Producer uses ` gather ` to
672
+ instantiate consumer tasks and wait on their completion.
673
+ 3 . ` Event ` s are replaced with a single [ Barrier] ( ./TUTORIAL.md#37-barrier )
674
+ instance.
675
+
676
+ Solution 1 suffers a proliferation of ` Event ` s and suffers an inefficient
677
+ busy-wait where the producer waits on N events. Solution 2 is inefficient with
678
+ constant creation of tasks. Arguably the ` Barrier ` class is the best approach.
691
679
692
680
** NOTE NOT YET SUPPORTED - see Message class**
693
681
An Event can also provide a means of communication between an interrupt handler
694
682
and a task. The handler services the hardware and sets an event which is tested
695
- in slow time by the task.
683
+ in slow time by the task. See [ PR6106 ] ( https://github.com/micropython/micropython/pull/6106 ) .
696
684
697
685
###### [ Contents] ( ./TUTORIAL.md#contents )
698
686
@@ -888,9 +876,9 @@ asyncio.run(queue_go(4))
888
876
889
877
## 3.6 Message
890
878
891
- This is an unofficial primitive and has no counterpart in CPython asyncio.
879
+ This is an unofficial primitive with no counterpart in CPython asyncio.
892
880
893
- This is a minor adaptation of the ` Event ` class. It provides the following:
881
+ This is similar to the ` Event ` class. It provides the following:
894
882
* ` .set() ` has an optional data payload.
895
883
* ` .set() ` is capable of being called from a hard or soft interrupt service
896
884
routine - a feature not yet available in the more efficient official ` Event ` .
@@ -927,11 +915,14 @@ async def main():
927
915
928
916
asyncio.run(main())
929
917
```
930
-
931
918
A ` Message ` can provide a means of communication between an interrupt handler
932
919
and a task. The handler services the hardware and issues ` .set() ` which is
933
920
tested in slow time by the task.
934
921
922
+ Currently its behaviour differs from that of ` Event ` where multiple tasks wait
923
+ on a ` Message ` . This may change: it is therefore recommended to use ` Message `
924
+ instances with only one receiving task.
925
+
935
926
###### [ Contents] ( ./TUTORIAL.md#contents )
936
927
937
928
## 3.7 Barrier
@@ -964,6 +955,47 @@ would imply instantiating a set of tasks on every pass of the loop.
964
955
passing a barrier does not imply return. ` Barrier ` now has an efficient
965
956
implementation using ` Event ` to suspend waiting tasks.
966
957
958
+ The following is a typical usage example. A data provider acquires data from
959
+ some hardware and transmits it concurrently on a number of interefaces. These
960
+ run at different speeds. The ` Barrier ` synchronises these loops. This can run
961
+ on a Pyboard.
962
+ ``` python
963
+ import uasyncio as asyncio
964
+ from primitives.barrier import Barrier
965
+ from machine import UART
966
+ import ujson
967
+
968
+ data = None
969
+ async def provider (barrier ):
970
+ global data
971
+ n = 0
972
+ while True :
973
+ n += 1 # Get data from some source
974
+ data = ujson.dumps([n, ' the quick brown fox jumps over the lazy dog' ])
975
+ print (' Provider triggers senders' )
976
+ await barrier # Free sender tasks
977
+ print (' Provider waits for last sender to complete' )
978
+ await barrier
979
+
980
+ async def sender (barrier , swriter , n ):
981
+ while True :
982
+ await barrier # Provider has got data
983
+ swriter.write(data)
984
+ await swriter.drain()
985
+ print (' UART' , n, ' sent' , data)
986
+ await barrier # Trigger provider when last sender has completed
987
+
988
+ async def main ():
989
+ sw1 = asyncio.StreamWriter(UART(1 , 9600 ), {})
990
+ sw2 = asyncio.StreamWriter(UART(2 , 1200 ), {})
991
+ barrier = Barrier(3 )
992
+ for n, sw in enumerate ((sw1, sw2)):
993
+ asyncio.create_task(sender(barrier, sw, n + 1 ))
994
+ await provider(barrier)
995
+
996
+ asyncio.run(main())
997
+ ```
998
+
967
999
Constructor.
968
1000
Mandatory arg:
969
1001
* ` participants ` The number of coros which will use the barrier.
@@ -972,8 +1004,8 @@ Optional args:
972
1004
* ` args ` Tuple of args for the callback. Default ` () ` .
973
1005
974
1006
Public synchronous methods:
975
- * ` busy ` No args. Returns ` True ` if at least one coro is waiting on the
976
- barrier, or if at least one non-waiting coro has not triggered it .
1007
+ * ` busy ` No args. Returns ` True ` if at least one task is waiting on the
1008
+ barrier.
977
1009
* ` trigger ` No args. The barrier records that the coro has passed the critical
978
1010
point. Returns "immediately".
979
1011
* ` result ` No args. If a callback was provided, returns the return value from
@@ -1000,36 +1032,6 @@ passed the barrier, and all waiting coros have reached it. At that point all
1000
1032
waiting coros will resume. A non-waiting coro issues ` barrier.trigger() ` to
1001
1033
indicate that is has passed the critical point.
1002
1034
1003
- ``` python
1004
- import uasyncio as asyncio
1005
- from uasyncio import Event
1006
- from primitives.barrier import Barrier
1007
-
1008
- def callback (text ):
1009
- print (text)
1010
-
1011
- async def report (num , barrier , event ):
1012
- for i in range (5 ):
1013
- # De-synchronise for demo
1014
- await asyncio.sleep_ms(num * 50 )
1015
- print (' {} ' .format(i), end = ' ' )
1016
- await barrier
1017
- event.set()
1018
-
1019
- async def main ():
1020
- barrier = Barrier(3 , callback, (' Synch' ,))
1021
- event = Event()
1022
- for num in range (3 ):
1023
- asyncio.create_task(report(num, barrier, event))
1024
- await event.wait()
1025
-
1026
- asyncio.run(main())
1027
- ```
1028
-
1029
- multiple instances of ` report ` print their result and pause until the other
1030
- instances are also complete and waiting on ` barrier ` . At that point the
1031
- callback runs. On its completion the tasks resume.
1032
-
1033
1035
###### [ Contents] ( ./TUTORIAL.md#contents )
1034
1036
1035
1037
## 3.8 Delay_ms class
0 commit comments