进程池与线程池
在介绍进程池和线程池之前,我们需要对其原理有一个了解。总所周知,进程和线程都是不可被重复利用的,在实现高并发中,这会极大的浪费资源。所以首先我们应该想办法实现线程和进程的可重复利用,而生产者与消费者模式就可以很好地解决这个问题。当我们需要开启多个线程或进程时,难道只能一个个去开吗?这时我们可以开辟一个进程池或线程池,可以一次就开启多个进程或线程,池的简单实现也可以用生产者与消费者模式来实现。最后在弄清楚基本实现原理,我们可以直接使用python自带的进程池和线程池。
可重复利用的线程池
利用生产者与消费者模式来实现线程的可重复利用,将主线程作为生产者,子线程作为消费者,让主线程调用来生产任务,子线程执行任务。
from threading import Thread
from queue import Queue
if __name__ == '__main__':
class MyThread(Thread):
def __init__(self,que):
super().__init__()
self.que = que
self.daemon = True
self.start()
def run(self): #子线程执行 ,充当消费者
while True:
task = self.que.get()
self.que.task_done()
task() #执行任务
def apply_async(self,func): #充当生产者,在主线程中调用
self.que.put(func) #生产任务
def join(self, timeout=None):
self.que.join()
def func1():
print("这是任务1")
def func2():
print("这是任务2")
que = Queue(10)
T = MyThread(que)
T.apply_async(func1)
T.apply_async(func2)
T.apply_async(func1)
T.apply_async(func2)
T.join() #等待队列结束
这是任务1
这是任务2
这是任务1
这是任务2
上述代码就是实现了一个线程重复执行多个任务,实现了线程的可重复利用。下面在上面代码加以改进,实现任务可传参。
from threading import Thread
from queue import Queue
if __name__ == '__main__':
class MyThread(Thread):
def __init__(self,que):
super().__init__()
self.que = que
self.daemon = True
self.start()
def run(self): #子线程执行 ,充当消费者
while True:
task,args,kwargs = self.que.get()
self.que.task_done()
task(*args,**kwargs) #执行任务
def apply_async(self,func,args = (),kwargs = {}): #充当生产者,在主线程中调用
self.que.put((func,args,kwargs)) #生产任务
def join(self, timeout=None):
self.que.join()
def func1(*args,**kwargs):
print("这是任务1,代号是%d"%args)
print(kwargs)
def func2(*args,**kwargs):
print("这是任务2,代号是%d"%args)
print(kwargs)
que = Queue(10)
T = MyThread(que)
T.apply_async(func1,args=(666,),kwargs={'name':'刘亦菲'})
T.apply_async(func2,args=(888,),kwargs={'name':'胡歌'})
T.apply_async(func1,args=(222,),kwargs={'name':'素素'})
T.apply_async(func2,args=(111,),kwargs={'name':'龙'})
T.join()
这是任务1,代号是666
{'name': '刘亦菲'}
这是任务2,代号是888
{'name': '胡歌'}
这是任务1,代号是222
{'name': '素素'}
这是任务2,代号是111
{'name': '龙'}
实现简单线程池
简单的线程池原理是让主线程充当生产者,只管向线程池提交任务,不管是哪个线程来执行,而线程池充当消费者,负责执行任务,将任务分配给空闲的子线程。
from threading import Thread
from queue import Queue
if __name__ == '__main__':
class ThreadPool():
def __init__(self,n):
self.que = Queue()
for i in range(n):
Thread(target=self.func,args=(self.que,),daemon = True).start()
def func(self,que):
while True:
task = que.get()
self.que.task_done()
task()
def apply_async(self,task):
self.que.put(task)
def join(self):
self.que.join()
def func1():
print("这是任务一")
def func2():
print("这是任务二")
p = ThreadPool(5) #开启5个线程
p.apply_async(func1)
p.apply_async(func2)
p.apply_async(func1)
p.apply_async(func2)
p.join()
这是任务一
这是任务二
这是任务一
这是任务二
python自带池
进程池和线程池创建用的是同一个模块(multiprocessing),但方法不同,进程池的方法是Pool,线程池的方法是pool.ThreadPool。不管是进程池还是线程池,它们都是默认开启守护模式的。下面以进程池为例:
- 实例化:pool = Pool(num),num是开启进程的数量。
- 提交任务:pool.apply_async(func[,args = (),kwds = {}])
- 关闭:pool.close(),关闭后就禁止再提交任务。
- join等待:pool.join(),只要队列计数器不为0,就一直阻塞。
- 中止进程:pool.terminate():中止进程池,中止所有任务。
multiprocessing.cpu_count()可以返回cpu数
任务函数的返回值怎么获取?
通过变量接收:async_esult = pool.apply_async(func) ,再用get方法获取返回值:result = async_esult.get()。这个方法get会阻塞,又返回结果就解阻塞。
下面是利用线程池来实现简单的操作
from multiprocessing.pool import ThreadPool
if __name__ == '__main__':
def func1(*args,**kwargs)
print("这是任务一")
return "hello"
def func2(*args,**kwargs):
print("这是任务二")
p = ThreadPool(2)
a = p.apply_async(func1) #获取任务返回值
print(a.get())
p.apply_async(func2)
a = p.apply_async(func1) #获取任务返回值
print(a.get())
p.apply_async(func2)
p.close()
p.join()
这是任务一
hello
这是任务二
这是任务一
hello
这是任务二
使用池来实现并发服务器
from multiprocessing.pool import ThreadPool
import socket
if __name__ == '__main__':
server = socket.socket()
server.bind(('127.0.0.5',8899))
server.listen(20)
p = ThreadPool(20)
def recv(coon,addr):
while True:
data = coon.recv(1024)
if data:
print("收到来自{}的信息:{}".format(addr,data.decode()))
coon.send("已收到信息:%s"%data)
else:
coon.close()
break
while True:
coon,addr = server.accept() #有一个连接过来就交给一个线程去执行
p.apply_async(recv,args=(coon,addr))