Recipe9.6.Coordinating Threads by Simple Message Passing


Recipe 9.6. Coordinating Threads by Simple Message Passing

Credit: Michael Hobbs

Problem

You want to write a multithreaded application, using, as the synchronization and communication primitive, a simple yet powerful message-passing paradigm.

Solution

The candygram module lets you use concurrent programming semantics that are essentially equivalent to those of the Erlang language. To use candygram, you start by defining appropriate classes, such as the following one, to model your threads' functionality:

import candygram as cg class ExampleThread(object):     """A thread-class with just a single counter value and a stop flag."""     def _ _init_ _(self):         """ Initialize the counter to 0, the running-flag to True. """         self.val = 0         self.running = True     def increment(self):         """ Increment the counter by one. """         self.val += 1     def sendVal(self, msg):         """ Send current value of counter to requesting thread. """         req = msg[0]         req.send((cg.self( ), self.val))     def setStop(self):         """ Set the running-flag to False. """         self.running = False     def run(self):         """ The entry point of the thread. """         # Register the handler functions for various messages:         r = cg.Receiver( )         r.addHandler('increment', self.increment)         r.addHandler((cg.Process, 'value'), self.sendVal, cg.Message)         r.addHandler('stop', self.setStop)         # Keep handling new messages until a stop has been requested         while self.running:             r.receive( )

To start a thread running this code under candygram, use:

counter = cg.spawn(ExampleThread( ).run)

To handle the counter tHRead's responses, you need another Receiver object, with the proper handler registered:

response = cg.Receiver( ) response.addHandler((counter, int), lambda msg: msg[1], cg.Message)

And here is an example of how you might use these counter and response objects:

# Tell thread to increment twice counter.send('increment') counter.send('increment') # Request the thread's current value, then print the thread's response counter.send((cg.self( ), 'value')) print response.receive( ) # Tell thread to increment one more time counter.send('increment') # Again, request the thread's current value, then print the thread's response counter.send((cg.self( ), 'value')) print response.receive( ) # Tell the thread to stop running counter.send('stop')

Discussion

With the candygram module (http://candygram.sourceforge.net), Python developers can send and receive messages between threads using semantics nearly identical to those introduced in the Erlang language (http://www.erlang.org). Erlang is widely respected for its elegant built-in facilities for concurrent programming.

Erlang's approach is simple and yet powerful. To communicate with another thread, simply send a message to it. You do not need to worry about locks, semaphores, mutexes, and other such primitives, to share information among concurrent tasks. Developers of multitasking software mostly use message passing only to implement a producer/consumer model. When you combine message passing with the flexibility of a Receiver object, however, it becomes much more powerful. For example, by using timeouts and message patterns, a thread may easily handle its messages as a state machine, or as a priority queue.

For those who wish to become more familiar with Erlang, http://www.erlang.org/download/erlang-book-part1.pdf (Concurrent Programming in Erlang) provides a very complete introduction. In particular, the candygram module implements all of the functions described in Chapter 5 and sections 7.2, 7.3, and 7.5 of that book.

This recipe offers a very elementary demonstration of how messages are passed between threads using candygram. When you run this recipe as a script, the print statements will output the values 2 and then 3.

It's important to understand how the candygram.Receiver class works. The addHandler method requires at least two parameters: the first is a message pattern and the second is a handler function. The Receiver.receive method invokes a registered handler function, and returns that function's result, whenever it finds a message that matches the associated pattern. Any parameters optionally passed to addHandler beyond the first two get passed as parameters to the handler function when the Receiver calls it. If a parameter is the candygram.Message constant, then receive replaces that parameter with the matching message when it calls the handler function.

This recipe's code contains four different message patterns: 'increment', (cg.Process, 'value'), 'stop', and (counter, int). The 'increment' and 'stop' patterns are simple patterns that match any message that consists solely of the strings 'increment' and 'stop', respectively. The (cg.Process, 'value') pattern matches any message that is a tuple with two items, where the first item isinstance of cg.Process and the second item is the string value. Lastly, the (counter, int) pattern matches any message that is a tuple with two items where the first item is the counter object and the second element is an integer.

You can find more information about the Candygram package at http://candygram.sourceforge.net. At that URL, you can find all details on how to specify message patterns, how to set a timeout for the Receiver.receive method, and how to monitor the running status of spawned threads.

See Also

Concurrent Programming in Erlang at http://www.erlang.org/download/erlang-book-part1.pdf; the candygram home page at http://candygram.sourceforge.net.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net