进程池、线程池、协程

进程池与线程池

硬件是有极限的,我们不可能一直在一台计算机上无限的创建新的进程和线程,虽然软件逻辑上我们可以无限的创建,但是一旦这么做了,我们的计算机可能到达承受不了的极限,最后崩溃到无法执行任何程序的地步。

所以为了限制进程和线程的无限创建(如我们写过的来一个客户端就开个进程去服务他),有了池的概念。

  • 进程池/线程池:提前创建好固定数量的进程/线程供后续程序的调用,超出数量则等待

代码实现进程池和线程池

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import os
import time
import random

# 1.产生含有固定数量线程的线程池
# pool = ThreadPoolExecutor(10)
pool = ProcessPoolExecutor(5)  # 产生进程池
# 查看源码可以看见这个类可以不写数字产生对象,这个数字是池的容量
# 如果不指定就根据cpu的参数来自动获取对应合适的数量


def task(n):
    print('task is running')
    time.sleep(random.randint(1, 3))

if __name__ == '__main__':
    # 2.将任务提交给线程池即可
    for i in range(20):
        pool.submit(task, 123)  # 朝线程池提交任务
        
"""
运行的结果会成批的打印,按照原本的开进程线程的方式,相当于同时开启了20个进程或线程
但现在当提交的任务达到进程或线程达到池的上限,新的任务就会等待某个任务线程执行完再去争夺线程\进程
"""

回调机制

回调机制指当提交到进程\线程池的任务结束时可以触发另一个函数的运行,并且将自己的任务对象传入函数以便它进行处理,如可以通过任务对象.result()的方式拿到任务函数的返回值。

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import os
import time
import random

# 1.产生含有固定数量线程的线程池
# pool = ThreadPoolExecutor(10)
pool = ProcessPoolExecutor(5)


def task(n):
    print('task is running')
    time.sleep(random.randint(1, 3))
    return '我是task函数的返回值'  # 拿到返回值


def func(self):
    print('from func', self.result())  # 在这里仍然异步的处理返回值。

if __name__ == '__main__':
    # 2.将任务提交给线程池即可
    for i in range(20):
        res = pool.submit(task, 123)  # 朝线程池提交任务,返回任务对象
        # print(res.result())  # 不能直接获取,相当于变成串行
        res.add_done_callback(func)  # 提交的任务可以调用这个函数来触发回调,当任务结束时调用func函数

协程

是独立于操作系统的进程线程外开发的实现并发的方式,也就是说可以通过单线程实现并发。

我们知道进程和线程是通过碰到IO时切换加保存程序状态的方式实现异步的,现在我们让程序帮我们检测IO,当碰到IO时就去做当前线程的其他代码,当那个IO结束时就处理相应的代码段,这个过程也一样实现了异步并发。这样做让操作系统永远不知道我们进入了IO,也可以提升我们程序对CPU的占用率。

协程代码实现

import time
from gevent import monkey;

monkey.patch_all()  # 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn


def func1():
    print('func1 running')
    time.sleep(3)
    print('func1 over')


def func2():
    print('func2 running')
    time.sleep(5)
    print('func2 over')


if __name__ == '__main__':
    start_time = time.time()
    # func1()
    # func2()
    s1 = spawn(func1)  # 检测代码 一旦有IO自动切换(执行没有io的操作 变向的等待io结束)
    s2 = spawn(func2)
    s1.join()
    s2.join()
    print(time.time() - start_time)  # 串行8.01237154006958   协程 5.015487432479858

协程实现并发服务端

import socket
from gevent import monkey;monkey.patch_all()  # 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn


def communication(sock):
    while True:
        data = sock.recv(1024)  # IO操作
        print(data.decode('utf8'))
        sock.send(data.upper())


def get_server():
    server = socket.socket()
    server.bind(('127.0.0.1', 8080))
    server.listen(5)
    while True:
        sock, addr = server.accept()  # IO操作
        spawn(communication, sock)  # 异步的接收用户输入

s1 = spawn(get_server)  # 创建socket套接字
s1.join()

上述代码中,如果任务中含IO操作,则就可以将其传入spwan让其进行协程处理。

ps:如果我们想要尽可能的提高程序的并发量,则要多开进程,进程下开多线程,线程下开协程。

原文地址:http://www.cnblogs.com/Leethon-lizhilog/p/16913177.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性