线程池与进程池
池是一个容器的概念,所谓进程池,线程池,就是建立好的进程或线程,在一个容器对象中,且可以重复使用。开启进程与线程都需要消耗资源,两者相比,线程消耗资源更少一些。但是为了能在计算机硬件承受范围内最大限度地利用计算机资源,可以使用线程池与进程池。同时进程池线程池限制了程序云效率,但是保证了计算机安全。
创建线程池与线程池都在concurrent.future模块下ThreadPoolExeutor(线程池),ProcessPoolExecutor(进程池)。首先需要进线程池创建对象,开启进线程时需要用到submit()方法,且都有返回结果,结果对象需要用到result()获取结果。进线程对象都有shutdown()方法关闭对象。

'''
线程池
两种方式
'''
import time
from concurrent.futures import ThreadPoolExecutor # 调用线程池
pool = ThreadPoolExecutor(5) # 创建线程池对象 线程创建个数默认是当前计算cup个数乘以5,现在设定5个
def func(n):
print(f'{n}来了')
time.sleep(2)
return f'{n}走了'
t_list = []
for i in range(20): # 开启20个线程
res = pool.submit(func, f'{i}号伞兵') # 开启线程, 获取结果对象
t_list.append(res) # 添加到列表中
for k in t_list:
print(k.result()) # 打印结果
'''
线程池处理的第二种方式
'''
import time
from concurrent.futures import ThreadPoolExecutor
# 调用线程池
pool = ThreadPoolExecutor(5) # 创建线程池对象
def func(n):
print(f'{n}来了')
time.sleep(2)
return f'{n}走了'
def call_back(m): # 返回结果处理函数
print(m.result())
for i in range(20):
pool.submit(func, f'{i}号伞兵').add_done_callback(call_back) # 使用add_done_callback设置返回结果处理
'''
进程池两种方式
'''
import time
from concurrent.futures import ProcessPoolExecutor
# 调用进程池
pool = ProcessPoolExecutor(5) # 创建进程池 默认是cup个数
def func(n):
print(f'{n}来了')
time.sleep(2)
return f'{n}走了'
if __name__ == '__main__':
t = []
for i in range(20):
res = pool.submit(func, f'{i}号伞兵')
t.append(res)
for k in t:
print(k.result())
pool.shutdown()
'''
创建进程第二种方式
'''
import time
from concurrent.futures import ProcessPoolExecutor
# 调用进程池
pool = ProcessPoolExecutor(5) # 创建进程池 默认是cup个数
def func(n):
print(f'{n}来了')
time.sleep(2)
return f'{n}走了'
def call_back(m):
print(m.result())
if __name__ == '__main__':
for i in range(20):
pool.submit(func, f'{i}号伞兵').add_done_callback(call_back)
协程
协程是程序员自定义一名词,是对单线程下实现并发,称之为协程。并发是一种现象:是主观上看起来计算机同时执行多个任务。实现并发计算的多道技术就是并发的一种体现,当进程遇到I/O操作时计算机便会保存当前状态,切换到下一个事件的计算。因此要做到切换和保存状态边可以实现并发。在python中使用genvent包中spawn和mokey模块,可以做到保存状态加切换。
import time
from gevent import spawn, monkey;monkey.patch_all() # monkey.patch_all() 检测I/O
def func1():
print(111)
time.sleep(1) # 模拟I/O阻塞
print(111)
def func2():
print(222)
time.sleep(2)
print(222)
spawn(func1) # spawn模块会检测函数中的I/O,检测到I/O状态,就会调到其他非阻塞
res = spawn(func2) # 检测func2
res.join() # 等待
利用协程实现单核下TCP并发通信
'''
服务端
'''
import socket
from gevent import spawn, monkey
monkey.patch_all()
server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)
def talk(conn):
while True:
try:
data = conn.recv(1024)
if len(data) == 0: break
print(data.decode('utf-8'))
conn.send(data.upper())
except BaseException as e:
print(e)
break
conn.close()
def server1():
while True:
conn, addr = server.accept()
spawn(talk, conn) # 检测talk函数,传入conn参数
if __name__ == '__main__':
res = spawn(server1) # 检测阻塞态
res.join()
'''
服务端
'''
import socket
from threading import Thread,current_thread
def clint1():
clint = socket.socket()
clint.connect(('127.0.0.1', 8081))
i = 0
while True:
msg = '%s--%s' % (current_thread().name, i)
clint.send(msg.encode('utf-8'))
data = clint.recv(1024)
print(data.decode('utf-8'))
i += 1
for i in range(400): # 开启400个线程,访问服务端
t = Thread(target=clint1)
print(current_thread().name)
t.start()
写成应用应根据任务不同来使用,对于计算密集型的任务来说,不适合用协程,但对于I/O密集型协程是适合的。
对于并发:可以开多进程,多进程下开多线程,多线程下还可以开多协程!!!
