Mastering Concurrency in Python
上QQ阅读APP看书,第一时间看更新

Starting a thread with the threading module

You now know how to start a thread with the thread module, and you know about its limited and low-level use of threading and the need for considerably unintuitive workarounds when working with it. In this subsection, we will explore the preferred threading module and its advantages over the thread module, with regard to the implementation of multithreaded programs in Python.

To create and customize a new thread using the threading module, there are specific steps that need to be followed:

  1. Define a subclass of the threading.Thread class in your program
  2. Override the default __init__(self [,args]) method inside of the subclass, in order to add custom arguments for the class
  3. Override the default run(self [,args]) method inside of the subclass, in order to customize the behavior of the thread class when a new thread is initialized and started

You actually saw an example of this in the first example of this chapter. As a refresher, the following is what we have to use to customize a threading.Thread subclass, in order to perform a five-step countdown, with a customizable delay between each step:

# Chapter03/my_thread.py

import threading
import time


class MyThread(threading.Thread):
def __init__(self, name, delay):
threading.Thread.__init__(self)
self.name = name
self.delay = delay

def run(self):
print('Starting thread %s.' % self.name)
thread_count_down(self.name, self.delay)
print('Finished thread %s.' % self.name)

def thread_count_down(name, delay):
counter = 5

while counter:
time.sleep(delay)
print('Thread %s counting down: %i...' % (name, counter))
counter -= 1

In our next example, we will look at the problem of determining whether a specific number is a prime number. This time, we will be implementing a multithreaded Python program through the threading module. Navigate to the Chapter03 folder and the example3.py file. Let's first focus on the MyThread class, as follows:

# Chapter03/example3.py

import threading

class MyThread(threading.Thread):
def __init__(self, x):
threading.Thread.__init__(self)
self.x = x

def run(self):
print('Starting processing %i...' % x)
is_prime(self.x)

Each instance of the MyThread class will have a parameter called x, specifying the prime number candidate to be processed. As you can see, when an instance of the class is initialized and started (that is, in the run(self) function), the is_prime() function, which is the same prime-checking function that we used in the previous example, on the x parameter, before that a message is also printed out by the run() function to specify the beginning of the processing.

In our main program, we still have the same list of input for prime-checking. We will be going through each number in that list, spawning and running a new instance of the MyThread class with that number, and appending that MyThread instance to a separate list. This list of created threads is necessary because, after that, we will have to call the join() method on all of those threads, which ensures that all of the threads have finished executing successfully:

my_input = [2, 193, 323, 1327, 433785907]

threads = []

for x in my_input:
temp_thread = MyThread(x)
temp_thread.start()

threads.append(temp_thread)

for thread in threads:
thread.join()

print('Finished.')

Notice that, unlike when we used the thread module, this time, we do not have to invent a workaround to make sure that all of the threads have finished executing successfully. Again, this is done by the join() method provided by the threading module. This is only one example of the many advantages of using the more powerful, higher-level API of the threading module, rather than using the thread module.