为什么编程的时候要使用进程、线程、协程?使用它们是为了进行多并发编程。那么为什么要进行多并发编程?因为多并发编程可以减少程序运行的时间,让用户获得更好的体验。
1.进程
概念:操作系统执行程序分配存储空间的最小单位。一个CPU只能同时处理一个进程。python实现多进程,使用multiprocessing模块的Process类来创建进程。具体代码如下:
from multiprocessing import Process
from os import getpid
import time
from random import randint
def download(filename):
"""
定义一个下载文件的方法
:param filename:要下载的文件名
:return:
"""
task = randint(8, 15)
time.sleep(task)
print("下载文件:%s,花费%s秒"%(filename,task))
def main():
#记录开始下载时间
start = time.time()
#创建一个进程,target为进程要执行的方法,args是一个元组,为调用该方法要传入的参数。
p1 = Process(target=download,args=("python自动化测试.pdf",))
#启动进程
p1.start()
p2 = Process(target=download,args=("测试技巧.pdf",))
p2.start()
#执行进程并等待进程执行结束
p1.join()
p2.join()
#记录下载完成时间
end = time.time()
#输出下载总耗时
print("下载总耗时%.2f秒"%(end-start))
if __name__ == "__main__":
main()
输出结果如下:
使用多进程的编程的好处是可以大大提高代码的执行效率,但是多进程存在一个问题,进程之前的通信比较复杂,实现起来会占用较大的资源,此时我们引入线程的来解决这个问题。
2.线程
一个进程可以包含多个线程,同一进程中的每个线程的信息是可以进行共享的。python使用threading模块的Thread类来创建新的线程。python的类是可以继承的,我们创建类时可以继承Thread类。具体代码如下:
from threading import Thread
from random import randint
import time
#创建Download类,继承Thread类
class Download(Thread):
def __init__(self, filename):
super().__init__()
self._filename = filename
def run(self):
task = randint(8, 13)
time.sleep(task)
print("下载文件:%s,花费%s秒"%(self._filename,task))
def close(self):
print("3sdf%s"%self._filename)
def main():
start = time.time()
t1 = Download("python自动化测试.pdf")
#执行t1.start时,会运行run方法
t1.start()
t2 = Download("测试技巧.pdf")
t2.start()
t1.join()
t2.join()
end = time.time()
print("下载完成,共耗时%.2f"%(end-start))
if __name__ == "__main__":
main()
输出结果如下:
在进行出入库操作时,多线程并发时,会导致数据丢失问题。我们写一段代码,库存值为100,启动50个线程同时进行入库操作,所有线程入库结束后。理论上最终的库存值为150。具体代码如下:
from threading import Thread
from random import randint
import time
class Repertory(object):
"创建仓库类"
def __init__(self):
#初始化仓库数量
self._initialize_number = 100
def add_number(self,number):
#入库操作
new_number = number+self._initialize_number
#模拟入库时间
time.sleep(0.01)
#更新仓库库存
self._initialize_number = new_number
@property
def number(self):
#返回仓库库存数量
return self._initialize_number
class AddNumberThread(Thread):
def __init__(self, repertory, number):
"""
:param repertory: 仓库对象
:param number: 新增库存数量
"""
super().__init__()
self._repertory = repertory
self._number = number
def run(self):
self._repertory.add_number(number=self._number)
def main():
repertory = Repertory()
threads = []
#创建50个线程进行入库操作
for _ in range(50):
t = AddNumberThread(repertory=repertory,number=1)
threads.append(t)
t.start()
#等待所有线程入库结束
for thread in threads:
thread.join()
#打印最终的库存数
print("仓库库存数%s"%repertory.number)
if __name__ == "__main__":
main()
输出结果如下:
运行结果与我们想象中的完全不一样,因为大部分线程启动的时候,self._initialize_number都等于100,要解决这个问题,就需要用到线程锁。在代码中加入锁,修改后的代码如下:
from threading import Thread, Lock
import time
class Repertory(object):
"创建仓库类"
def __init__(self):
#初始化仓库数量
self._initialize_number = 100
#定义锁
self._lock = Lock()
def add_number(self,number):
#拿到锁才能执行下面代码
self._lock.acquire()
try:
#入库操作
new_number = number+self._initialize_number
#模拟入库时间
time.sleep(0.01)
#更新仓库库存
self._initialize_number = new_number
finally:
#代码执行完成后,释放锁
self._lock.release()
@property
def number(self):
#返回仓库库存数量
return self._initialize_number
class AddNumberThread(Thread):
def __init__(self, repertory, number):
"""
:param repertory: 仓库对象
:param number: 新增库存数量
"""
super().__init__()
self._repertory = repertory
self._number = number
def run(self):
self._repertory.add_number(number=self._number)
def main():
repertory = Repertory()
threads = []
#创建50个线程进行入库操作
for _ in range(50):
t = AddNumberThread(repertory=repertory,number=1)
threads.append(t)
t.start()
#等待所有线程入库结束
for thread in threads:
thread.join()
#打印最终的库存数
print("仓库库存数%s"%repertory.number)
if __name__ == "__main__":
main()
输入结果如下: