Home Accessibility Courses Diary The Mouth Forum Resources Site Map About Us Contact
 
For 2023 (and 2024 ...) - we are now fully retired from IT training.
We have made many, many friends over 25 years of teaching about Python, Tcl, Perl, PHP, Lua, Java, C and C++ - and MySQL, Linux and Solaris/SunOS too. Our training notes are now very much out of date, but due to upward compatability most of our examples remain operational and even relevant ad you are welcome to make us if them "as seen" and at your own risk.

Lisa and I (Graham) now live in what was our training centre in Melksham - happy to meet with former delegates here - but do check ahead before coming round. We are far from inactive - rather, enjoying the times that we are retired but still healthy enough in mind and body to be active!

I am also active in many other area and still look after a lot of web sites - you can find an index ((here))
Python threads - a first example

If you have a process that you want to do several things at the same time, threads may be the answer for you. They let you set up a series of processes (or sub-processes) each of which can be run independently, but which can be brought back together later and/or co-ordinated as they run.

For many applications, threads are overkill but on some occasions they can be useful.

A PYTHON APPLICATION WHERE THREADS WOULD HELP

Let's say that you want to check the availability of many computers on a network ... you'll use ping. But there's a problem - if you "ping" a host that's not running it takes a while to timeout, so that when you check through a whole lot of systems that aren't responding - the very time a quick response is probably needed - it can take an age.

Here's a Python program that "ping"s 10 hosts in sequence.

import os
import re
import time
import sys

lifeline = re.compile(r"(\d) received")
report = ("No response","Partial Response","Alive")

print time.ctime()

for host in range(60,70):
   ip = "192.168.200."+str(host)
   pingaling = os.popen("ping -q -c2 "+ip,"r")
   print "Testing ",ip,
   sys.stdout.flush()
   while 1:
      line = pingaling.readline()
      if not line: break
      igot = re.findall(lifeline,line)
      if igot:
           print report[int(igot[0])]

print time.ctime()

Running that program, it works but it's a bit slow:

[trainee@buttercup trainee]$ python alive
Mon May 9 05:22:51 2005
Testing 192.168.200.60 No response
Testing 192.168.200.61 No response
Testing 192.168.200.62 No response
Testing 192.168.200.63 No response
Testing 192.168.200.64 No response
Testing 192.168.200.65 No response
Testing 192.168.200.66 Alive
Testing 192.168.200.67 No response
Testing 192.168.200.68 No response
Testing 192.168.200.69 No response
Mon May 9 05:23:19 2005
[trainee@buttercup trainee]$

That was 28 seconds - in other words, an extra 3 seconds per unavailable host.

THE SAME APPLICATION, WRITTEN USING PYTHON THREADS

I'll write the application and test it first ... then add a few notes at the bottom.

import os
import re
import time
import sys
from threading import Thread

class testit(Thread):
   def __init__ (self,ip):
      Thread.__init__(self)
      self.ip = ip
      self.status = -1
   def run(self):
      pingaling = os.popen("ping -q -c2 "+self.ip,"r")
      while 1:
        line = pingaling.readline()
        if not line: break
        igot = re.findall(testit.lifeline,line)
        if igot:
           self.status = int(igot[0])

testit.lifeline = re.compile(r"(\d) received")
report = ("No response","Partial Response","Alive")

print time.ctime()

pinglist = []

for host in range(60,70):
   ip = "192.168.200."+str(host)
   current = testit(ip)
   pinglist.append(current)
   current.start()

for pingle in pinglist:
   pingle.join()
   print "Status from ",pingle.ip,"is",report[pingle.status]

print time.ctime()

And running:

[trainee@buttercup trainee]$ python kicking
Mon May 9 05:23:36 2005
Status from 192.168.200.60 is No response
Status from 192.168.200.61 is No response
Status from 192.168.200.62 is No response
Status from 192.168.200.63 is No response
Status from 192.168.200.64 is No response
Status from 192.168.200.65 is No response
Status from 192.168.200.66 is Alive
Status from 192.168.200.67 is No response
Status from 192.168.200.68 is No response
Status from 192.168.200.69 is No response
Mon May 9 05:23:39 2005
[trainee@buttercup trainee]$

3 seconds - much more acceptable than the 28 seconds that we got when we pinged the hosts one by one and waited on each.

HOW DOES IT WORK?

We're going to run code concurrently to ping each host computer and (being Python), we create an object for each of the concurrent tests (threads) we wish to run. Each of these objects inherits from the Thread class so that we can use all of the logic already written in Python to provide our parallelism.

Although the constructor builds the thread, it does not start it; rather, it leaves the thread based object at the starting gate. The start method on the testit object actually triggers it off - internally, the start method triggers the run method of the testit class and alse returns to the calling code. In other words, this is the point at which the parallel processing actually starts, and the run method is called indirectly (via a callback, in a similar way to sort and map make callbacks).

Once parallel processes have started, you'll want some way to bring the responses back together at the end and in this first simple example, I've used a join. Note that this is NOT the same join method that you have in the string class ;-)

A join waits for a run method to terminate. So, having spawned a series of 10 pings in our example, our code waits for them to finish .... and it waits in the order that they were started. Some will be queuing up and completed long before our loop of joins gets to them, but that doesn't matter!

LOOKING AHEAD - MORE ON THREADS

If you're going to be writing a threaded application, there are (broadly) two main approaches you can take. The example that I've shown above uses a separate thread to take each request through from beginning to end and all the threads have the same structure. An alternative strategy is to write a series of "worker" threads each of which performs a step in a multistep process, and have them passing data on to one another. It's the difference between having an employee to walk each order through your factory and having employees at a production line passing each job on.

Where you're running threads, you have to be very much aware of the effect they can have on one another. As soon as you have two workers, their work may interfere with one another and you get involved in synchronisation (locking) of objects to ensure that this doesn't happen.

Locking / synchronisation brings its own further complications in that you have to involve "deadlocks" where two threads both require two resources ... each grabs the first, then waits for the second which (because the other has it) never becomes available.

FOOTNOTES

We've used the operating system's ping process in this example program. Ping responses vary between operating systems and you may need to alter the ping command and regular expression to match the response. The example above has been tested on Fedora Core Linux.

Threading makes heavy use of Operating System capabilities and is NOT as portable (no matter what language you're programming in) than most code


See also Learning Python

Please note that articles in this section of our web site were current and correct to the best of our ability when published, but by the nature of our business may go out of date quite quickly. The quoting of a price, contract term or any other information in this area of our website is NOT an offer to supply now on those terms - please check back via our main web site

Related Material

Additional Python Facilities
  [183] - ()
  [208] - ()
  [239] - ()
  [463] - ()
  [663] - ()
  [672] - ()
  [753] - ()
  [901] - ()
  [1043] - ()
  [1136] - ()
  [1149] - ()
  [1305] - ()
  [1336] - ()
  [1337] - ()
  [1876] - ()
  [2407] - ()
  [2435] - ()
  [2462] - ()
  [2655] - ()
  [2721] - ()
  [2745] - ()
  [2746] - ()
  [2764] - ()
  [2765] - ()
  [2786] - ()
  [2790] - ()
  [3089] - ()
  [3442] - ()
  [3469] - ()
  [4085] - ()
  [4211] - ()
  [4298] - ()
  [4439] - ()
  [4451] - ()
  [4536] - ()
  [4593] - ()
  [4709] - ()

Python - Threads - What why and how
  [903] - ()
  [2489] - ()

resource index - Python
Solutions centre home page

You'll find shorter technical items at The Horse's Mouth and delegate's questions answered at the Opentalk forum.

At Well House Consultants, we provide training courses on subjects such as Ruby, Lua, Perl, Python, Linux, C, C++, Tcl/Tk, Tomcat, PHP and MySQL. We're asked (and answer) many questions, and answers to those which are of general interest are published in this area of our site.

You can Add a comment or ranking to this page

© WELL HOUSE CONSULTANTS LTD., 2024: Well House Manor • 48 Spa Road • Melksham, Wiltshire • United Kingdom • SN12 7NY
PH: 01144 1225 708225 • FAX: 01144 1225 793803 • EMAIL: info@wellho.net • WEB: http://www.wellho.net • SKYPE: wellho

PAGE: http://www.wellho.info/solutions/python-p ... ample.html • PAGE BUILT: Wed Mar 28 07:47:11 2012 • BUILD SYSTEM: wizard