- 4.1. Introduction/Motivation
- 4.2. Threads and Processes
- 4.3. Threads and Python
- 4.4. The thread Module
- 4.5. The threading Module
- 4.6. Comparing Single vs. Multithreaded Execution
- 4.7. Multithreading in Practice
- 4.8. Producer-Consumer Problem and the Queue/queue Module
- 4.9. Alternative Considerations to Threads
- 4.10. Related Modules
- 4.11. Exercises
4.6. Comparing Single vs. Multithreaded Execution
The mtfacfib.py script, presented in Example 4-8 compares execution of the recursive Fibonacci, factorial, and summation functions. This script runs all three functions in a single-threaded manner. It then performs the same task by using threads to illustrate one of the advantages of having a threading environment.
Example 4-8. Fibonacci, Factorial, Summation (mtfacfib.py)
In this MT application, we execute three separate recursive functions—first in a single-threaded fashion, followed by the alternative with multiple threads.
1 #!/usr/bin/env python 2 3 from myThread import MyThread 4 from time import ctime, sleep 5 6 def fib(x): 7 sleep(0.005) 8 if x < 2: return 1 9 return (fib(x-2) + fib(x-1)) 10 11 def fac(x): 12 sleep(0.1) 13 if x < 2: return 1 14 return (x * fac(x-1)) 15 16 def sum(x): 17 sleep(0.1) 18 if x < 2: return 1 19 return (x + sum(x-1)) 20 21 funcs = [fib, fac, sum] 22 n = 12 23 24 def main(): 25 nfuncs = range(len(funcs)) 26 27 print '*** SINGLE THREAD' 28 for i in nfuncs: 29 print 'starting', funcs[i].__name__, 'at:', 30 ctime() 31 print funcs[i](n) 32 print funcs[i].__name__, 'finished at:', 33 ctime() 34 35 print '\n*** MULTIPLE THREADS' 36 threads = [] 37 for i in nfuncs: 38 t = MyThread(funcs[i], (n,), 39 funcs[i].__name__) 40 threads.append(t) 41 42 for i in nfuncs: 43 threads[i].start() 44 45 for i in nfuncs: 46 threads[i].join() 47 print threads[i].getResult() 48 49 print 'all DONE' 50 51 if __name__ == '__main__': 52 main()
Running in single-threaded mode simply involves calling the functions one at a time and displaying the corresponding results right after the function call.
When running in multithreaded mode, we do not display the result right away. Because we want to keep our MyThread class as general as possible (being able to execute callables that do and do not produce output), we wait until the end to call the getResult() method to finally show you the return values of each function call.
Because these functions execute so quickly (well, maybe except for the Fibonacci function), you will notice that we had to add calls to sleep() to each function to slow things down so that we can see how threading can improve performance, if indeed the actual work had varying execution times—you certainly wouldn’t pad your work with calls to sleep(). Anyway, here is the output:
$ mtfacfib.py *** SINGLE THREAD starting fib at: Wed Nov 16 18:52:20 2011 233 fib finished at: Wed Nov 16 18:52:24 2011 starting fac at: Wed Nov 16 18:52:24 2011 479001600 fac finished at: Wed Nov 16 18:52:26 2011 starting sum at: Wed Nov 16 18:52:26 2011 78 sum finished at: Wed Nov 16 18:52:27 2011 *** MULTIPLE THREADS starting fib at: Wed Nov 16 18:52:27 2011 starting fac at: Wed Nov 16 18:52:27 2011 starting sum at: Wed Nov 16 18:52:27 2011 fac finished at: Wed Nov 16 18:52:28 2011 sum finished at: Wed Nov 16 18:52:28 2011 fib finished at: Wed Nov 16 18:52:31 2011 233 479001600 78 all DONE