线程池和进程池
线程安全
单线程程序不会有线程安全的问题,但多线程程序就可能出现线程安全的问题,举个例子,当多个线程同时访问同一个数据时并对其做出操作时,可能会出错,原因是线程一访问某个数据并修改了它,但还没来得及保存,线程二也访问了这个数据,但取到的数据还是未修改的,所以线程二取到的是一个脏数据(错误数据)。
线程安全解决方案——加锁
1.Lock锁
from threading import Thread,Lock#导入线程模块和Lock锁模块
account = 10000#定义一个全局变量,表示账户余额
account_lock = Lock()#创建锁对象,一个数据对应一个锁对象
#定义一个存钱的函数
def save_money(num):
print(f'开始存钱')
global account
account_lock.acquire()#加锁
account += num
print(f'存钱成功,当前余额:{account}')
account_lock.release()#解锁
#定义一个取钱函数
def draw_money(num):
print(f'开始存钱')
global account
account_lock.acquire()#加锁
if account < num:
return
account -= num
print(f'存钱成功,当前余额:{account}')
account_lock.release()#解锁
#创建子线程对象
t1 = Thread(target=save_money,args=(2000,))
t2 = Thread(target=draw_money,args=(5000,))
#启动子线程
t1.start()
t2.start()
运行结果
H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/02-Lock锁.py
开始取钱
开始存钱
取钱成功,当前余额:5000
存钱成功,当前余额:7000
Process finished with exit code 0
2.RLock锁
from threading import Thread,RLock
import time
account = 10000
account_lock = RLock()
def save_money(num):
#RLock锁只需要加锁,with作用域的代码执行完后会自动解锁
with account_lock:
print('开始存钱')
global account
balance = account
time.sleep(2)
account = num + balance
print(f'存钱成功,当前余额:{account}')
def draw_money(num):
with account_lock:
print('开始取钱')
global account
balance = account
if balance >= num:
account = balance - num
time.sleep(2)
print(f'取钱成功,当前余额:{account}')
else:
print('余额不足')
t1 = Thread(target=save_money,args=(2000,))
t2 = Thread(target=draw_money,args=(5000,))
t2.start()
t1.start()
运行结果同上
队列
队列是一个容器,而且和迭代器比较像,只能一个一个往外去数据,和迭代器不同是队列除了有将数据放进去的进口,还有将数据取出的出口,相当于一个管道,也就是说队列的数据是先进先出,而迭代器里的数据是先进后出,队列里的数据无法遍历,队列还有一个特点,就是在用死循环取队列的数据时不会报错,因为在取完队列数据后队列会进入等待状态,等到有数据放进队列就继续取,如下:
from queue import Queue
q = Queue#创建队列对象
for i in range(10):
q.put(i)#放数据
while True:
num = q.get()#取数据
print(num)
运行结果:代码不会报错,但也不会结束,队列在死循环中会一直等
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JopjABdi-1604494280484)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20201104200844753.png)]
线程池
线程池可以理解为将多个子线程统一管理工具
实例代码
from threadpool import ThreadPool,makeRequests
import time
#定义一个函数
def func(a):
time.sleep(1)
print(a)
#创建线程池对象
t_pool = ThreadPool(10)#线程池有10个子线程
#创建任务列表/makeRequests(任务对应的函数,函数的参数列表)
task_lst = makeRequests(func,[x for x in range(10)])
#在线程池中添加任务
for task in task_lst:
t_pool.putRequest(task)
start_time = time.time()
t_pool.wait()#执行和等待
end_time = time.time()
print(f'执行时间:{end_time-start_time}s')
运行结果:其执行时间是单线程执行时间的1/10
H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/test.py
6
4
3
2
1
0
5
8
9
7
执行时间:1.0014607906341553s
Process finished with exit code 0
进程池
进程池的概念和线程池一样,进程池就是多个子进程的管理工具,不同之处在于线程池里的每个线程可以访问并操作同一个数据,而进程池不行,进程池在创建时,会在每个子进程里拷贝一份主进程的数据,子进程只能操作拷贝的数据,无法操作主进程的数据
from multiprocessing import Pool
import time
def func(a):
time.sleep(1)
print(a)
if __name__=='__main__':
#创建进程池对象
p_pool = Pool(3)#3个子进程
start_time = time.time()
#添加任务
p_pool.map(func,[x for x in range(9)])#添加多个任务
#p_pool.apply(func,(10,))#添加一个任务
#p_pool.apply_async(func,(11,))#添加一个任务,不过是和主进程异步执行,前两个都是和主进程同步执行
"""线程池添加任务后会自动启动"""
p_pool.close()#关闭线程池
p_pool.join()#等待线程池的任务执行完成
end_time = time.time()
print(f'执行时间:{end_time-start_time}s')
运行结果:执行时间是单进程执行时间的1/3
H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/test.py
0
1
2
3
4
5
6
7
8
执行时间:3.5766236782073975s
Process finished with exit code 0
执行时间是单进程执行时间的1/3
H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/test.py
0
1
2
3
4
5
6
7
8
执行时间:3.5766236782073975s
Process finished with exit code 0