在上一章中,学习了 Python 多进程编程的一些基本方法:使用跨平台多进程模块 multiprocessing 提供的 Process、Pool、Queue、Lock、Pipe 等类,实现子进程创建、进程池(批量创建子进程并管理子进程数量上限)以及进程间通信。这一章学习下 Python 下的多线程编程方法。
线程是操作系统执行任务的最小单元。Python 标准库中提供了 threading 模块,对多线程编程提供了很便捷的支持。
下面是使用 threading 实现多线程的代码:
- 1 #!/usr/bin/python
- 2 # -*- coding: utf-8 -*
- 3 __author__ = 'zni.feng'
- 4 import sys
- 5 reload (sys)
- 6 sys.setdefaultencoding('utf-8')
- 7
- 8 import threading, time
- 9
- 10 def test(index):
- 11 print time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
- 12 print 'thread %s starts.' % threading.current_thread().name
- 13 print 'the index is %d' % index
- 14 time.sleep(3)
- 15 print time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
- 16 print 'thread %s ends.' % threading.current_thread().name
- 17
- 18 if __name__ == "__main__":
- 19 print time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
- 20 print 'thread %s starts.' % threading.current_thread().name
- 21 #创建线程
- 22 my_thread = threading.Thread(target = test, args=(1,) , name= 'zni_feng_thread')
- 23 #等待2s
- 24 time.sleep(2)
- 25 #启动线程
- 26 my_thread.start()
- 27 #等待线程结束
- 28 my_thread.join()
- 29 print time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
- 30 print 'thread %s ends.' % threading.current_thread().name
输出结果为:
- 2017-01-12 22:06:32
- thread MainThread starts.
- 2017-01-12 22:06:34
- thread zni_feng_thread starts.
- the index is 1
- 2017-01-12 22:06:37
- thread zni_feng_thread ends.
- 2017-01-12 22:06:37
- thread MainThread ends.
- [Finished in 5.1s]
其中,threading 模块的 current_thread() 函数会返回当前线程的实例。
多进程与多线程的最大不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响。而多线程中,所有变量都由所有线程共享,所以,任何一个共享变量都可以被任何一个线程修改。因此线程之间共享数据最大的危险在于多个线程同时改变一个变量。为了解决这个问题,我们可以借助于 threading 模块的 Lock 类给共享变量加锁。
先看看使用多线程写同一个共享变量,不加锁的例子:
- 1 #!/usr/bin/python
- 2 # -*- coding: utf-8 -*
- 3 __author__ = 'zni.feng'
- 4 import sys
- 5 reload (sys)
- 6 sys.setdefaultencoding('utf-8')
- 7 import threading
- 8
- 9 class Account:
- 10 def __init__(self):
- 11 self.balance = 0
- 12
- 13 def add(self):
- 14 for i in range(0,100000):
- 15 self.balance += 1
- 16
- 17 def delete(self):
- 18 for i in range(0,100000):
- 19 self.balance -=1
- 20
- 21 if __name__ == "__main__":
- 22 account = Account()
- 23 #创建线程
- 24 thread_add = threading.Thread(target=account.add, name= 'Add')
- 25 thread_delete = threading.Thread(target=account.delete, name= 'Delete')
- 26
- 27 #启动线程
- 28 thread_add.start()
- 29 thread_delete.start()
- 30
- 31 #等待线程结束
- 32 thread_add.join()
- 33 thread_delete.join()
- 34
- 35 print 'The final balance is: ' + str(account.balance)
运行结果为:
- The final balance is: -51713
- [Finished in 0.1s]
可以发现,每次运行,它的最终结果都会不同,而且都不是 0。就是因为不同线程在同时修改同一个变量时,发生了冲突,某些中间变量没有按顺序被使用导致。
现在我们使用 Lock 对程序进行加锁:
- 1 #!/usr/bin/python
- 2 # -*- coding: utf-8 -*
- 3 __author__ = 'zni.feng'
- 4 import sys
- 5 reload (sys)
- 6 sys.setdefaultencoding('utf-8')
- 7 import threading
- 8
- 9 class Account:
- 10 def __init__(self):
- 11 self.balance = 0
- 12
- 13 def add(self, lock):
- 14 lock.acquire()
- 15 for i in range(0,100000):
- 16 self.balance += 1
- 17 lock.release()
- 18
- 19 def delete(self, lock):
- 20 lock.acquire()
- 21 for i in range(0,100000):
- 22 self.balance -=1
- 23 lock.release()
- 24
- 25
- 26 if __name__ == "__main__":
- 27 account = Account()
- 28 lock = threading.Lock()
- 29 #创建线程
- 30 thread_add = threading.Thread(target=account.add, args=(lock, ), name= 'Add')
- 31 thread_delete = threading.Thread(target=account.delete, args=(lock, ), name= 'Delete')
- 32
- 33 #启动线程
- 34 thread_add.start()
- 35 thread_delete.start()
- 36
- 37 #等待线程结束
- 38 thread_add.join()
- 39 thread_delete.join()
- 40
- 41 print 'The final balance is: ' + str(account.balance)
可以发现,无论如何执行多少次,balance 结果都为 0。如果将每次 balance 计算的结果都打印出来,还会发现,当一个线程开始执行时,另一个线程一定会等到前一个线程执行完(准确地说是 lock.release() 执行完)后才开始执行。
- The final balance is: 0
- [Finished in 0.1s]
来源: