threading 模块建立在低级 thread 之上,使线程更容易和更加pythonic。 使用线程允许程序在同一进程空间中同时运行多个操作(并发)。

Thread Objects

最简单的方法是使用 Thread 实例,它使用带 target参数的回调函数,并使用 start()来开始工作

  1. import threading
  2. def worker():
  3. """thread worker function"""
  4. print 'Worker'
  5. return
  6. threads = []
  7. for i in range(5):
  8. t = threading.Thread(target=worker) # 实例化 Thread对象,传递 target
  9. threads.append(t)
  10. t.start() # 启动线程

它能够生成一个线程并传递它的参数来告诉它要做什么工作是很有用的。这个例子传递一个数字,然后线程打印这个数字。

  1. import threading
  2. def worker(num):
  3. """thread worker function"""
  4. print 'Worker: %s' % num
  5. return
  6. threads = []
  7. for i in range(5):
  8. t = threading.Thread(target=worker, args=(i,)) # 传递 args参数,元组
  9. threads.append(t)
  10. t.start()
  11. '''
  12. Worker: 0
  13. Worker: 1
  14. Worker: 2
  15. Worker: 3
  16. Worker: 4
  17. '''

确定当前线程

使用参数来识别或命名线程是麻烦的,也是不必要的。 每个Thread实例都有一个名称,其默认值可以在创建线程时更改。 命名线程在服务器进程中很有用,其中多个服务线程处理不同的操作。

  1. import threading
  2. import time
  3. def worker():
  4. print threading.currentThread().getName(), 'Starting'
  5. time.sleep(2)
  6. print threading.currentThread().getName(), 'Exiting'
  7. def my_service():
  8. print threading.currentThread().getName(), 'Starting'
  9. time.sleep(3)
  10. print threading.currentThread().getName(), 'Exiting'
  11. t = threading.Thread(name='my_service', target=my_service)
  12. w = threading.Thread(name='worker', target=worker)
  13. w2 = threading.Thread(target=worker) # use default name
  14. w.start()
  15. w2.start()
  16. t.start()
  17. '''
  18. =============== RESTART: C:/Users/lite/python/pythreaddemo.py ===============
  19. workerThread-1
  20. >>> my_service startingstartingstarting
  21. Thread-1worker exitingexiting
  22. my_service exiting
  23. '''

logging 是线程安全的,所以来自不同线程的消息在输出中保持不同。

daemon vs non_daemon Threads

  1. import threading
  2. import time
  3. import logging
  4. logging.basicConfig(level=logging.DEBUG,
  5. format='(%(threadName)-10s) %(message)s')
  6. def daemon():
  7. logging.debug("Starting")
  8. time.sleep(2)
  9. logging.debug("exiting")
  10. d = threading.Thread(target=daemon, name='daemon')
  11. d.setDaemon(True)
  12. def non_daemon():
  13. logging.debug('starting')
  14. logging.debug('exiting')
  15. t = threading.Thread(target=non_daemon, name='non_daemon')
  16. d.start()
  17. t.start()
  18. '''
  19. D:\projects\pythoncode\pythonparallel
  20. (pythonparallel-jU7W1wcf) λ python pythreaddemo.py
  21. (daemon ) Starting
  22. (non_daemon) starting
  23. (non_daemon) exiting
  24. D:\projects\pythoncode\pythonparallel
  25. '''

请注意,输出不包含来自守护程序线程的“exiting”消息,因为所有非守护程序线程(包括主线程)在守护程序线程从其第二次休眠状态唤醒之前退出。

要等到守护程序线程完成其工作,请使用join()方法。

  1. import threading
  2. import time
  3. import logging
  4. logging.basicConfig(level=logging.DEBUG,
  5. format='(%(threadName)-10s) %(message)s')
  6. def daemon():
  7. logging.debug("Starting")
  8. time.sleep(2)
  9. logging.debug("exiting")
  10. d = threading.Thread(target=daemon, name='daemon')
  11. d.setDaemon(True)
  12. def non_daemon():
  13. logging.debug('starting')
  14. logging.debug('exiting')
  15. t = threading.Thread(target=non_daemon, name='non_daemon')
  16. d.start()
  17. t.start()
  18. d.join()
  19. t.join()
  20. '''
  21. D:\projects\pythoncode\pythonparallel
  22. (pythonparallel-jU7W1wcf) λ python pythreaddemo.py
  23. (daemon ) Starting
  24. (non_daemon) starting
  25. (non_daemon) exiting
  26. (daemon ) exiting
  27. D:\projects\pythoncode\pythonparallel
  28. '''

默认情况下,join()无限期地阻塞。 也可以传递一个超时参数(一个表示等待线程变为非活动状态的秒数的浮点数)。 如果线程在超时期限内没有完成,则join()仍会返回。

  1. join(timeout=None)
  2. Wait until the thread terminates. This blocks the calling thread until the thread whose join() method is called terminates either normally or through an unhandled exception or until the optional timeout occurs.
  3. When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call is_alive() after join() to decide whether a timeout happened if the thread is still alive, the join() call timed out.
  4. When the timeout argument is not present or None, the operation will block until the thread terminates.
  5. A thread can be join()ed many times.
  6. join() raises a RuntimeError if an attempt is made to join the current thread as that would cause a deadlock. It is also an error to join() a thread before it has been started and attempts to do so raise the same exception.

等到线程终止。 这会阻塞调用线程,直到调用join()方法的线程终止

  • 正常或通过未处理的异常
  • 或直到发生可选的超时。

当timeout参数存在而不是None时,它应该是一个浮点数,指定操作的超时(以秒为单位)(或其中的分数)。 由于join()始终返回None,因此必须在join()之后调用is_alive()来确定是否发生超时 - 如果线程仍处于活动状态,则join()调用超时。
当timeout参数不存在或None时,操作将阻塞,直到线程终止。
一个线程可以多次join()。
如果尝试加入当前线程,join()会引发RuntimeError,因为这会导致死锁。 在线程启动之前 join() 线程也是一个错误,并且尝试这样做会引发相同的异常。

  1. import threading
  2. import time
  3. import logging
  4. logging.basicConfig(level=logging.DEBUG,
  5. format='(%(threadName)-10s) %(message)s')
  6. def daemon():
  7. logging.debug("Starting")
  8. time.sleep(2)
  9. logging.debug("exiting")
  10. d = threading.Thread(target=daemon, name='daemon')
  11. d.setDaemon(True)
  12. def non_daemon():
  13. logging.debug('starting')
  14. logging.debug('exiting')
  15. t = threading.Thread(target=non_daemon, name='non_daemon')
  16. d.start()
  17. t.start()
  18. d.join(1) # 由于传递的超时时间小于守护程序线程休眠的时间,因此在join()返回后线程仍处于“活动状态”。
  19. print('d isAlive(): ', d.isAlive())
  20. t.join()

由于传递的超时时间小于守护程序线程休眠的时间,因此在join()返回后线程仍处于“活动状态”。

枚举所有线程

没有必要为所有守护程序线程保留显式句柄,以确保它们在退出主进程之前已完成。 enumerate()返回活动Thread实例的列表。 该列表包括当前线程,并且由于不允许加入当前线程(它引入了死锁情况),因此必须跳过它。