Python入门学习 DAY37(进程池和线程池 协程 单线程实现并发)

Python入门学习

DAY37

今日内容:

进程池和线程池

协程(gevent)

单线程实现并发的套接字

1. 进程池和线程池

多进程是实现并发的手段之一,需要注意的问题是:

  1. 很明显需要并发执行的任务通常要远大于核数

  2. 一个操作系统不可能无限开启进程,通常有几个核就开几个进程

  3. 进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行)

此时我们就可以通过维护一个进程池来控制进程数目,规定进程数

当我们在通过

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

扫描二维码关注公众号,回复: 3137923 查看本文章

导入线程池和进程池的模块时,需要提交任务给创建的进程池调用

提交任务有两种方式:

同步调用:提交一个任务之后,就在原地等待,等待任务完完整整的运行完毕拿到结果后,再执行下一行代码,会导致任务是串行执行

异步调用:提交一个任务之后,不在原地等待,而是直接执行下一行代码,会导致任务是并发执行

在进程池中还有一个回调函数的概念

回调函数的应用场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,那么该函数即回调函数,如果在主进程中等待进程池中所有任务都执行完毕后,再统一处理结果,则无需回调函数

回调函数的作业:我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了IO的过程,直接拿到的是任务的结果。

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from threading import current_thread
import time
import requests

def get(url):
    print('%s GET %s' % (current_thread().name, url))
    time.sleep(3)
    response = requests.get(url)
    if response.status_code == 200:
        res = response.text
    else:
        res = '下载失败'
    return res

def parse(future):
    time.sleep(1)
    res = future.result()
    print('%s 解析 %s' % (current_thread().name, len(res)))

if __name__ == '__main__':
    urls = [
        'http://www.baidu.com',
        'http://www.sina.com.cn',
        'http://www.tmall.com',
        'http://www.jd.com',
        'http://www.python.org',
        'http://www.baidu.com',
        'http://www.baidu.com',
    ]
    p = ThreadPoolExecutor(7)
    for url in urls:
        future = p.submit(get, url)
        # 异步调用:结果futrue对象会在任务运行完毕后自动传给回调函数
        future.add_done_callback(parse)  # parse 会在任务运行完毕后自动触发,然后接收一个参数future对象
        # 回调函数是由主进程运行的
    p.shutdown(wait=True)

    print('主', current_thread().name)

2.协程(gevent模块)

协程目标:
        在线程下实现并发
        并发(多个任务看起来是同时执行就是并发):切换+保存状态

协程概念:
    协程是单线程实现并发
    注意:协程是程序员意淫出来的,操作系统里只有进程和线程的概念(操作系统调度的是线程)

    在单线程下实现多个任务间遇到IO就切换可以降低单线程的IO时间,从而最大限度地提升单线程的效率

对比操作系统控制线程的切换,用户在单线程内控制协程的切换

优点如下:

1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
2. 单线程内就可以实现并发的效果,最大限度地利用cpu

缺点如下:

1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

gevent模块

gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程

#用法
g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的

g2=gevent.spawn(func2)

g1.join() #等待g1结束

g2.join() #等待g2结束

#或者上述两步合作一步:gevent.joinall([g1,g2])

g1.value#拿到func1的返回值

例题:

from gevent import monkey
monkey.patch_all()
from gevent import spawn,joinall
import time
# 模拟边玩边吃的程序
def play(name):
    print('%s play 1' % name)
    time.sleep(3)
    print('%s play 2' % name)

def eat(name):
    print('%s eat 1' % name)
    time.sleep(5)
    print('%s eat 2' % name)

start = time.time()
g1 = spawn(play, 'xxx')
g2 = spawn(eat, 'xxx')
# g1.join()   # 不加join 主线程会直接结束,程序就会结束运行,得不到想要的效果
# g2.join()
joinall([g1,g2])
print('主',time.time() - start)

3. 单线程实现并发的套接字

服务端

from gevent import monkey, spawn

monkey.patch_all()
from socket import *


def comunicate(conn):
    while True:
        try:
            data = conn.recv(1024)
            conn.send(data.upper())

        except ConnectionResetError:
            break
    conn.close()


def server(ip, port, backlog=5):
    server = socket(AF_INET, SOCK_STREAM)
    server.bind((ip, port))
    server.listen(backlog)

    while True:
        conn, client_addr = server.accept()
        print(client_addr)
        spawn(comunicate(conn))


if __name__ == '__main__':
    g1 = spawn(server, '127.0.0.1', 8080)
    g1.join()

客户端


from threading import  Thread,current_thread
from socket import *


def client():
    '''客户端连接发送函数'''
    client = socket(AF_INET,SOCK_STREAM)
    client.connect(('127.0.0.1',8080))
    n = 0
    while True:
        msg= '%s say holle %s'%(current_thread().name,n)
        n+=1
        client.send(msg.encode('utf-8'))
        data = client.recv(1024)
        print(data)

if __name__ == '__main__':
    for i in range(100):
        t = Thread(target=client)
        t.start()

以上为本次学习内容

猜你喜欢

转载自blog.csdn.net/sql121407/article/details/82597555