Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy does Python's math.factorial not play nice with threads?
    text
    copied!<p>Why does math.factorial act so weird in a thread?</p> <p>Here is an example, it creates three threads: </p> <ul> <li>thread that just sleeps for a while</li> <li>thread that increments an int for a while</li> <li>thread that does math.factorial on a large number.</li> </ul> <p>It calls <code>start</code> on the threads, then <code>join</code> with a timeout</p> <p>The sleep and spin threads work as expected and return from <code>start</code> right away, and then sit in the <code>join</code> for the timeout.</p> <p>The factorial thread on the other hand does not return from <code>start</code> until it runs to the end!</p> <pre class="lang-py prettyprint-override"><code>import sys from threading import Thread from time import sleep, time from math import factorial # Helper class that stores a start time to compare to class timed_thread(Thread): def __init__(self, time_start): Thread.__init__(self) self.time_start = time_start # Thread that just executes sleep() class sleep_thread(timed_thread): def run(self): sleep(15) print "st DONE:\t%f" % (time() - time_start) # Thread that increments a number for a while class spin_thread(timed_thread): def run(self): x = 1 while x &lt; 120000000: x += 1 print "sp DONE:\t%f" % (time() - time_start) # Thread that calls math.factorial with a large number class factorial_thread(timed_thread): def run(self): factorial(50000) print "ft DONE:\t%f" % (time() - time_start) # the tests print print "sleep_thread test" time_start = time() st = sleep_thread(time_start) st.start() print "st.start:\t%f" % (time() - time_start) st.join(2) print "st.join:\t%f" % (time() - time_start) print "sleep alive:\t%r" % st.isAlive() print print "spin_thread test" time_start = time() sp = spin_thread(time_start) sp.start() print "sp.start:\t%f" % (time() - time_start) sp.join(2) print "sp.join:\t%f" % (time() - time_start) print "sp alive:\t%r" % sp.isAlive() print print "factorial_thread test" time_start = time() ft = factorial_thread(time_start) ft.start() print "ft.start:\t%f" % (time() - time_start) ft.join(2) print "ft.join:\t%f" % (time() - time_start) print "ft alive:\t%r" % ft.isAlive() </code></pre> <p>And here is the output on Python 2.6.5 on CentOS x64:</p> <pre><code>sleep_thread test st.start: 0.000675 st.join: 2.006963 sleep alive: True spin_thread test sp.start: 0.000595 sp.join: 2.010066 sp alive: True factorial_thread test ft DONE: 4.475453 ft.start: 4.475589 ft.join: 4.475615 ft alive: False st DONE: 10.994519 sp DONE: 12.054668 </code></pre> <p>I've tried this on python 2.6.5 on CentOS x64, 2.7.2 on Windows x86 and the factorial thread does not return from start on either of them until the thread is done executing.</p> <p>I've also tried this with PyPy 1.8.0 on Windows x86, and there result is slightly different. The start <em>does</em> return immediately, but then the join doesn't time out!</p> <pre><code>sleep_thread test st.start: 0.001000 st.join: 2.001000 sleep alive: True spin_thread test sp.start: 0.000000 sp DONE: 0.197000 sp.join: 0.236000 sp alive: False factorial_thread test ft.start: 0.032000 ft DONE: 9.011000 ft.join: 9.012000 ft alive: False st DONE: 12.763000 </code></pre> <p>Tried IronPython 2.7.1 too, it produces the expected result.</p> <pre><code>sleep_thread test st.start: 0.023003 st.join: 2.028122 sleep alive: True spin_thread test sp.start: 0.003014 sp.join: 2.003128 sp alive: True factorial_thread test ft.start: 0.002991 ft.join: 2.004105 ft alive: True ft DONE: 5.199295 sp DONE: 5.734322 st DONE: 10.998619 </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload