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