加载中...

12.1 启动与停止线程


问题

You want to create and destroy threads for concurrent execution of code.

解决方案

The threading library can be used to execute any Python callable in its own thread. Todo this, you create a Thread instance and supply the callable that you wish to executeas a target. Here is a simple example:

Code to execute in an independent threadimport timedef countdown(n):

while n > 0:print(‘T-minus', n)n -= 1time.sleep(5)

Create and launch a threadfrom threading import Threadt = Thread(target=countdown, args=(10,))t.start()

When you create a thread instance, it doesn’t start executing until you invoke its start()method (which invokes the target function with the arguments you supplied).Threads are executed in their own system-level thread (e.g., a POSIX thread or Windowsthreads) that is fully managed by the host operating system. Once started, threads runindependently until the target function returns. You can query a thread instance to seeif it’s still running:

if t.is_alive():print(‘Still running')else:print(‘Completed')
You can also request to join with a thread, which waits for it to terminate:

t.join()

The interpreter remains running until all threads terminate. For long-running threadsor background tasks that run forever, you should consider making the thread daemonic.For example:

t = Thread(target=countdown, args=(10,), daemon=True)t.start()

Daemonic threads can’t be joined. However, they are destroyed automatically when themain thread terminates.Beyond the two operations shown, there aren’t many other things you can do withthreads. For example, there are no operations to terminate a thread, signal a thread,adjust its scheduling, or perform any other high-level operations. If you want thesefeatures, you need to build them yourself.If you want to be able to terminate threads, the thread must be programmed to poll forexit at selected points. For example, you might put your thread in a class such as this:

class CountdownTask:def init(self):self._running = Truedef terminate(self):self._running = Falsedef run(self, n):while self._running and n > 0:print(‘T-minus', n)n -= 1time.sleep(5)
c = CountdownTask()t = Thread(target=c.run, args=(10,))t.start()...c.terminate() # Signal terminationt.join() # Wait for actual termination (if needed)

Polling for thread termination can be tricky to coordinate if threads perform blockingoperations such as I/O. For example, a thread blocked indefinitely on an I/O operationmay never return to check if it’s been killed. To correctly deal with this case, you’ll needto carefully program thread to utilize timeout loops. For example:

class IOTask:def terminate(self):self._running = Falsedef run(self, sock):

sock is a socketsock.settimeout(5) # Set timeout periodwhile self._running:

Perform a blocking I/O operation w/ timeouttry:

data = sock.recv(8192)break

except socket.timeout:continue> # Continued processing...

Terminatedreturn

讨论

Due to a global interpreter lock (GIL), Python threads are restricted to an executionmodel that only allows one thread to execute in the interpreter at any given time. Forthis reason, Python threads should generally not be used for computationally intensivetasks where you are trying to achieve parallelism on multiple CPUs. They are muchbetter suited for I/O handling and handling concurrent execution in code that performsblocking operations (e.g., waiting for I/O, waiting for results from a database, etc.).Sometimes you will see threads defined via inheritance from the Thread class. Forexample:

from threading import Thread

class CountdownThread(Thread):def init(self, n):super().init()self.n = 0def run(self):
while self.n > 0:

print(‘T-minus', self.n)self.n -= 1time.sleep(5)

c = CountdownThread(5)c.start()

Although this works, it introduces an extra dependency between the code and thethreading library. That is, you can only use the resulting code in the context of threads,whereas the technique shown earlier involves writing code with no explicit dependencyon threading. By freeing your code of such dependencies, it becomes usable in othercontexts that may or may not involve threads. For instance, you might be able to executeyour code in a separate process using the multiprocessing module using code like this:

import multiprocessingc = CountdownTask(5)p = multiprocessing.Process(target=c.run)p.start()...

Again, this only works if the CountdownTask class has been written in a manner that isneutral to the actual means of concurrency (threads, processes, etc.).


还没有评论.