第29天 进程池与线程池 协程代码演示Gevent

进程池与线程池
计算机的性能是有限的,不能无限的开启进程和线程。
创建进程池,把并发任务的个数控制在计算机可承受范围之内

进程池(计算密集型用多进程)
方案1

方案1:等进程全部运行完毕统一拿返回值,需要处理返回值结果时采用并发的形式去处理结果效率最高,比较占用计算机的资源
from concurrent.futures import ProcessPoolExecutor
import os, time, random


def task(n):
    print("%s is running" % os.getpid())
    time.sleep(random.randint(1, 3))
    return n ** 2


if __name__ == '__main__':
    # print(os.cpu_count())     # 查看计算机的cpu是几核的,本机是8核的
    pool = ProcessPoolExecutor(
        max_workers=8)  # 主进程开启进程池,ProcessPoolExecutor()如果不传参的话计算机的cpu是几核的,那么就开几个进程,传参的话,传多少值就开几个进程。
    l = []
    for i in range(19):  # 进程池里面有8个进程,从始至终都是者8个进程,进程池里面的进程不会运行完一个就直接结束掉,会等这19个任务全部运行完毕才会结束进程
        future = pool.submit(task, i)  # .submit是异步的方式提交任务,task是需要运行的函数,i是传入task的参数
        l.append(future)

    pool.shutdown(wait=True)  # shutdown(统计进程池里面的进程个数,不允许在往进程池里面提交新的任务了),wait=True,每运行完一个任务就会减一,直到任务减成0,等待所有任务运行完毕
    print("主")
    for obj in l:
        print(obj.result())     # obj.result()等任务运行完毕统一拿返回值,这种方式采用并发的形式处理结果效率最高

方案2

方案2:利用回调函数让主进程去处理结果,每运行完一个进程,主进程就开启一个线程,这样对计算机资源的利用比较合理
from concurrent.futures import ProcessPoolExecutor
import os, time, random


def task(n):
    print("%s is running" % os.getpid())
    time.sleep(random.randint(1, 3))
    return n ** 2


def handle(futrue):
    res = futrue.result()
    print("%s 正在处理结果:%s" % (os.getpid(), res))
    time.sleep(2)


if __name__ == '__main__':
    pool = ProcessPoolExecutor(max_workers=8)

    for i in range(19):
        pool.submit(task, i).add_done_callback(
            handle)  # .add_done_callback()回调函数,是指用来处理任务的函数,一旦一个任务运行完,会把运行完的结果当初参数传给被调的函数,会自动触发回调函数的运行

    pool.shutdown(wait=True)

线程池代码演示(io密集型用多线程)

from concurrent.futures import ThreadPoolExecutor
from threading import current_thread
import time, random


def task(n):
    print("%s is running" % current_thread().name)
    time.sleep(random.randint(1, 3))
    return n ** 2


def handle(futrue):
    res = futrue.result()
    print("%s 正在处理结果:%s" % (current_thread().name, res))
    time.sleep(2)


if __name__ == '__main__':
    pool = ThreadPoolExecutor(max_workers=10)  # ThreadPoolExecutor()如果不传参数的话,开启线程的个数默认时cpu个数的5for i in range(19):
        pool.submit(task, i).add_done_callback(
            handle)  # .submit异步提交,.add_done_callback()回调函数,是指用来处理任务的函数,一旦一个任务运行完,会把运行完的结果当初参数传给被调的函数,会自动触发回调函数的运行

    pool.shutdown(wait=True)

协程就是单线程下实现的并发 (单线程最多可以抗住500多的并发)
特点:自己的应用程序实现多个人的调度
遇到io切换,可以将单线程的io降低到最低,提升单线程的运行效率
缺点:
1.不能实现并行
2.单线程下的多个任务一旦遇到io,整个线程都会阻塞,所有任务都会停滞

2、协程序的目的:
想要在单线程下实现并发
并发指的是多个任务看起来是同时运行的
并发=切换+保存状态

协程代码演示
下载安装gevent第三方库(cmd命令行里面输入命令pip3 install gevent)
协程代码演示Gevent(底层原理是封装了yield,遇到io保存当下的状态后会自动切换)
from gevent import monkey;monkey.patch_all()  # monkey.patch_all()会把代码所有的io行为转换成gevent能够识别的
import gevent
import time


def eat(name):
    print("%s eat 1" % name)
    time.sleep(2)  # 遇到io后切换到play
    print("%s eat 2" % name)


def play(name):
    print("%s play 1" % name)
    time.sleep(1)  # 遇到io后切换到eat,play也在io状态会进行来回的切换
    print("%s play 2" % name)


g1 = gevent.spawn(eat, "nana")
g2 = gevent.spawn(play, name="nana")
g1.join()  # print("主")运行完单线程就运行结束了,所有要用join让线程等待协程运行完再结束
g2.join()
print("主")

套接字程序实现协程并发案例

服务端
from gevent import monkey;monkey.path_all()
from socket import *
import gevent


def server(server_ip, port):        # 链接请求
    s = socket(AF_INET, SOCK_STREAM)
    # s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    s.bind((server_ip, port))   # 绑定ip跟端口
    s.listen(5)     # 监听请求
    while True:
        conn, addr = s.accept()     # 建立链接
        gevent.spawn(talk, conn, addr)  # 每建立一个链接提交一个协程对象,建立通信连接(异步提交)


def talk(conn, addr):       # 通信请求
    try:
        while True:
            res = conn.recv(1024)
            print("client %s:%s msg:%s" % (addr[0], addr[1], res))
            conn.send(res.upper())
    except Exception as e:
        print(e)
    finally:
        conn.close()


if __name__ == '__main__':
    server("127.0.0.1", 8080)
客户端
__author__ = "上海吴彦祖"

from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect(("127.0.0.1", 8080))

while True:
    msg = input("输入:").strip()
    if not msg: continue

    client.send(msg.encode("utf-8"))
    msg = client.recv(1024)
    print(msg.decode("utf-8"))

猜你喜欢

转载自blog.csdn.net/Yosigo_/article/details/113409214
今日推荐