Python 主要通过标准库中的 threading 包来实现多线程。在当今网络时代,每个服务器都会接收到大量的请求。服务器可以利用多线程的方式来处理这些请求,以提高对网络端口的读写效率。Python 是一种网络服务器的后台工作语言 (比如),所以多线程也就很自然被 Python 语言支持。
(关于多线程的原理和 C 实现方法,请参考我之前写的,要了解 race condition, mutex 和 condition variable 的概念)
我们使用 Python 来实现文中的售票程序。我们使用 mutex (也就是 Python 中的 Lock 类对象) 来实现线程的同步:
- # A program to simulate selling tickets in multi-thread way
- # Written by Vamei
- import threading
- import time
- import os
- # This function could be any function to do other chores.
- def doChore():
- time.sleep(0.5)
- # Function for each thread
- def booth(tid):
- global i
- global lock
- while True:
- lock.acquire() # Lock; or wait if other thread is holding the lock
- if i != 0:
- i = i - 1 # Sell tickets
- print(tid,':now left:',i) # Tickets left
- doChore() # Other critical operations
- else:
- print("Thread_id",tid," No more tickets")
- os._exit(0) # Exit the whole process immediately
- lock.release() # Unblock
- doChore() # Non-critical operations
- # Start of the main function
- i = 100 # Available ticket number
- lock = threading.Lock() # Lock (i.e., mutex)
- # Start 10 threads
- for k in range(10):
- new_thread = threading.Thread(target=booth,args=(k,)) # Set up thread; target: the callable (function) to be run, args: the argument for the callable
- new_thread.start() # run the thread
我们使用了两个全局变量,一个是 i,用以储存剩余票数;一个是 lock 对象,用于同步线程对 i 的修改。此外,在最后的 for 循环中,我们总共设置了 10 个线程。每个线程都执行 booth() 函数。线程在调用 start() 方法的时候正式启动 (实际上,计算机中最多会有 11 个线程,因为主程序本身也会占用一个线程)。Python 使用 threading.Thread 对象来代表线程,用 threading.Lock 对象来代表一个互斥锁 (mutex)。
有两点需要注意:
上面的 Python 程序非常类似于一个面向过程的 C 程序。我们下面介绍如何通过面向对象 (OOP, object-oriented programming,参看和) 的方法实现多线程,其核心是继承 threading.Thread 类。我们上面的 for 循环中已经利用了 threading.Thread() 的方法来创建一个 Thread 对象,并将函数 booth() 以及其参数传递给改对象,并调用 start() 方法来运行线程。OOP 的话,通过修改 Thread 类的 run() 方法来定义线程所要执行的命令。
- # A program to simulate selling tickets in multi-thread way
- # Written by Vamei
- import threading
- import time
- import os
- # This function could be any function to do other chores.
- def doChore():
- time.sleep(0.5)
- # Function for each thread
- class BoothThread(threading.Thread):
- def __init__(self, tid, monitor):
- self.tid = tid
- self.monitor = monitor
- threading.Thread.__init__(self)
- def run(self):
- while True:
- monitor['lock'].acquire() # Lock; or wait if other thread is holding the lock
- if monitor['tick'] != 0:
- monitor['tick'] = monitor['tick'] - 1 # Sell tickets
- print(self.tid,':now left:',monitor['tick']) # Tickets left
- doChore() # Other critical operations
- else:
- print("Thread_id",self.tid," No more tickets")
- os._exit(0) # Exit the whole process immediately
- monitor['lock'].release() # Unblock
- doChore() # Non-critical operations
- # Start of the main function
- monitor = {'tick':100, 'lock':threading.Lock()}
- # Start 10 threads
- for k in range(10):
- new_thread = BoothThread(k, monitor)
- new_thread.start()
我们自己定义了一个类 BoothThread, 这个类继承自 thread.Threading 类。然后我们把上面的 booth() 所进行的操作统统放入到 BoothThread 类的 run() 方法中。注意,我们没有使用全局变量声明 global,而是使用了一个 monitor 存放全局变量,然后把词典作为参数传递给线程函数。由于词典是可变数据对象,所以当它被传递给函数的时候,函数所使用的依然是同一个对象,相当于被多个线程所共享。这也是多线程乃至于多进程编程的一个技巧 (应尽量避免上面的 global 声明的用法,因为它并不适用于 windows 平台)。
上面 OOP 编程方法与面向过程的编程方法相比,并没有带来太大实质性的差别。
threading.Thread 对象: 我们已经介绍了该对象的 start() 和 run(), 此外:
下面的对象用于处理多线程同步。对象一旦被建立,可以被多个线程共享,并根据情况阻塞某些进程。请与中的同步工具参照阅读。
threading.Lock 对象: mutex, 有 acquire() 和 release() 方法。
threading.Condition 对象: condition variable,建立该对象时,会包含一个 Lock 对象 (因为 condition variable 总是和 mutex 一起使用)。可以对 Condition 对象调用 acquire() 和 release() 方法,以控制潜在的 Lock 对象。此外:
threading.Thread
Lock, Condition, Semaphore, Event
来源: