Welcome, guest | Sign In | My Account | Store | Cart

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

The beauty of the Erlang system is that it is simple and yet powerful. To communicate with another thread, you simply send a message to it. You do not need to worry about locks, semaphores, mutexes, etc. to share information among concurrent tasks. Developers mostly use message passing only to implement the 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 package implements all of the functions described in chapter 5 and sections 7.2, 7.3, and 7.5 of that book.

Python, 57 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import candygram as cg


class Thread:

    """A thread class with just a single counter value and a stop flag."""

    def __init__(self):
        """Initialize counter to zero and stop flag to False."""
        self.val = 0
        self.stop = False

    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):
        """Trip the stop flag."""
        self.stop = True

    def run(self):
        """Entry point of 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 the stop flag is set.
        while not self.stop:
            r.receive()
        # end while


# Create new thread.
counter = cg.spawn(Thread().run)
# Define a receiver that will return the thread's response values:
response = cg.Receiver()
response.addHandler((counter, int), lambda msg: msg[1], cg.Message)
# Tell thread to increment twice.
counter.send('increment')
counter.send('increment')
# Request thread's current value.
counter.send((cg.self(), 'value'))
# Print the response
print response.receive()
# Tell thread to increment one more time.
counter.send('increment')
# And print it's current value.
counter.send((cg.self(), 'value'))
print response.receive()
# Tell thread to stop.
counter.send('stop')

This is a very basic demonstration of how messages are passed between threads using Candygram. When run, the print statements will output the values 2 and then 3.

The most important thing to understand is how the candygram.Receiver class works. The addHandler() method takes two parameters; the first is a message pattern and the second is a handler function. The Receiver.receive() method invokes the handler function and returns its result whenever it finds a message that matches the associated pattern. Any parameters passed to addHandler() after the handler function are passed as parameters to the handler function when it is invoked. If a parameter is the candygram.Message constant, then receive() replaces the parameter with the matching message when it calls the handler function.

The demo code above contains 4 different message patterns: 'increment', (cg.Process, 'value'), 'stop', and (counter, int). The 'increment' and 'stop' patterns are very simple patterns that will match any message that consists solely of the strings 'increment' and 'stop'. The (cg.Process, 'value') pattern will match any message that is a 2-tuple and where the first element isinstance() of cg.Process and the second element is the string 'value'. Lastly, the (counter, int) pattern will match any message that is a 2-tuple where the first element is the `counter' object and the second element is an integer.

More information about the Candygram package can be found at http://candygram.sourceforge.net. There, you can find more information about 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.

1 comment

Petro Sasnyk 15 years, 9 months ago  # | flag

Integration with GUI and event frameworks. The conception which proposed by candygram is look very attractive, but when i'm try to implement real application on it i'm face with the problem of integration two event loops: one in wxPython and one provided by candygram. How to vercome this difficulty.