Marko Rauhamaa <ma...@pacujo.net>: > The asyncio module comes with coroutine support. Investigating the > topic on the net reveals that FSM's are for old people and the brave > new world uses coroutines. Unfortunately, all examples I could find > seem to be overly simplistic, and I'm left thinking coroutines have > few practical uses in network programming. > > [...] > > but how would I modify the philosopher code to support such master > resets?
Ok. I think I have found the answer to my question: asyncio.wait(coroutines, return_when=asyncio.FIRST_COMPLETED) That facility makes it possible to multiplex between several stimuli. The code below implements a modified dining philosophers protocol. The philosophers are accompanied by an assistant who occasionally prods the philosophers to immediately resume thinking, thus breaking the deadlock. Whether the coroutine style is easier on the eye than, say, callbacks and state machines is a matter of personal opinion. Marko ===clip-clip-clip======================================================= #!/usr/bin/env python3 import os, sys, asyncio, random, enum, time T0 = time.time() def main(): loop = asyncio.get_event_loop() try: fork1 = Fork() fork2 = Fork() fork3 = Fork() nag = Nag() loop.run_until_complete(asyncio.wait([ Philosopher("Plato", fork1, fork2, nag).philosophize(), Philosopher("Nietsche", fork2, fork3, nag).philosophize(), Philosopher("Hintikka", fork3, fork1, nag).philosophize(), assistant(nag) ])) finally: loop.close() class Philosopher: def __init__(self, name, left, right, nag): self.name = name self.left = left self.right = right self.nag = nag @asyncio.coroutine def philosophize(self): yield from self.nag.acquire() try: while True: self.nag_count = self.nag.count pending = yield from self.think() if pending is None: continue pending = yield from self.grab_fork("left", self.left, pending) if pending is None: continue try: pending = yield from self.wonder_absentmindedly(pending) if pending is None: continue pending = yield from self.grab_fork( "right", self.right, pending) if pending is None: continue try: pending = yield from self.dine(pending) if pending is None: continue finally: self.say("put back right fork") self.right.release() pending = yield from self.wonder_absentmindedly(pending) if pending is None: continue finally: self.say("put back left fork") self.left.release() finally: self.nag.release() def say(self, message): report("{} {}".format(self.name, message)) def nagged(self): return self.nag.count > self.nag_count @asyncio.coroutine def think(self): self.say("thinking") result, pending = yield from multiplex( identify(Impulse.TIME, random_delay()), identify(Impulse.NAG, self.nag.wait_for(self.nagged))) if result is Impulse.NAG: self.say("nagged") return None assert result is Impulse.TIME self.say("hungry") return pending @asyncio.coroutine def grab_fork(self, which, fork, pending): self.say("grabbing {} fork".format(which)) result, pending = yield from multiplex( identify(Impulse.FORK, fork.acquire()), *pending) if result is Impulse.NAG: self.say("has been nagged") return None assert result is Impulse.FORK self.say("got {} fork".format(which)) return pending @asyncio.coroutine def wonder_absentmindedly(self, pending): self.say("now, what was I doing?") result, pending = yield from multiplex( identify(Impulse.TIME, random_delay()), *pending) if result is Impulse.NAG: self.say("nagged") return None assert result is Impulse.TIME self.say("oh, that's right!") return pending @asyncio.coroutine def dine(self, pending): self.say("eating") result, pending = yield from multiplex( identify(Impulse.TIME, random_delay()), *pending) if result is Impulse.NAG: self.say("nagged") return None assert result is Impulse.TIME self.say("that hit the spot!") return pending @asyncio.coroutine def assistant(nag): n = 1 while True: report("assistant sleep {}".format(n)) yield from asyncio.sleep(n) report("assistant nag") yield from nag.nag() n += 1 def report(info): sys.stdout.write("{:9.3f} {}\n".format(time.time() - T0, info)) class Impulse(enum.Enum): TIME = 1 NAG = 2 FORK = 3 class Fork(asyncio.Lock): pass class Nag(asyncio.Condition): count = 0 @asyncio.coroutine def nag(self): yield from self.acquire() try: self.count += 1 self.notify_all() finally: self.release() @asyncio.coroutine def random_delay(): yield from asyncio.sleep(random.randint(1, 100) / 10) @asyncio.coroutine def identify(result, coroutine): yield from coroutine return result @asyncio.coroutine def multiplex(*coroutines): done, pending = yield from asyncio.wait( coroutines, return_when=asyncio.FIRST_COMPLETED) return done.pop().result(), pending if __name__ == '__main__': main() ===clip-clip-clip======================================================= -- https://mail.python.org/mailman/listinfo/python-list