并发编程之协程(进程池与线程池)

一,进程池与线程池

在系统能承受的范围内不可以无限的新建进程或者线程,这样就需要引进进程池或者线程池

(进程池与线程池的用法类似,这里就以进程池做例子)

提交任务的两种方式:
    同步调用:提交完一个任务之后,就在原地等待,等待任务完完整整的运行完毕拿到结果后,再执行下一行代码,会导致任务是串行的.
    异步调用:提交完一个任务之后,不在原地等待,而是直接执行下一行代码,会导致任务是并发执行的而结果等任务执行完毕之后自动将返回结果传给下一个函数当参数

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor   # 引进进程池与线程池的模块
import time,random,os

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

if __name__ == '__main__':
    print(os.cpu_count())   # 系统cpu的个数
    p = ProcessPoolExecutor(4)   #进程池中进程的个数 ,不加数是默认计算机cpu的数量(而线程的默认值是cpu数乘以5)

    l = []
    for i in range(10):
        # 同步提交(用的比较少,只要知道存在这样的用法就可以了)
        # res = p.submit(tast,'进程pid:',i).result
        # print(res)
        # 异步提交
        future = p.submit(task,'进程Pid:',i)
        l.append(future)

    p.shutdown(wait=True)       # 关闭进程池的入口并且等待进程池里面的进程全部执行完毕之后

    for future in l:
        print(future.result())
    print('主')
这样产生一个问题,需要等待所有的进城全部结束之后才能拿到进程的执行结果,再由主进程进行输出,下面引进一种方法,拿到返回的结果可以直接传入下一个函数

下载网址里面的内容:

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

def get(url):
    print('%s get %s' %(os.getpid(),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' %(os.getpid(),len(res)))


if __name__ == '__main__':
    urls = [
        'https://www.baidu.com',
        'https://www.sina.com.cn',
        'https://www.tmall.com',
        'https://www.jd.com',
        'https://www.python.org',
        'https://www.openstack.org',
        'https://www.baidu.com',
        'https://www.baidu.com',
        'https://www.baidu.com',
    ]
    p = ProcessPoolExecutor(4)
    start = time.time()
    for url in urls:
        future  = p.submit(get,url)
        future.add_done_callback(parse)
  #等待future完全执行完毕之后将future的返回值当做参数返回传给parse函数,并自动触发函数的执行
    p.shutdown(wait=True)
   # 关闭进程池的入口,并且在原地等待进程池里面的进程全部运行完毕
    print('主',time.time()-start)
    print('主',os.getpid())

二,基于多线程实现套接字

服务端

from socket import *
from threading import Thread

def communitation(conn):
    while True:  # 通讯循环
        try:
            res = conn.recv(1024)
            if not res: break
            print(res.decode('utf-8'))
            conn.send(res.upper())
        except ConnectionResetError:
            break
    conn.close()

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

    while True:   # 链接循环
        conn,client_addr = server.accept()
        print(client_addr)

        # 进行通讯
        t = Thread(target=communitation,args=conn)
        t.start()


if __name__ == '__main__':
    s = Thread(target=link,args=('127.0.0.1',8080))
    s.start()

客户端

from socket import *

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

while True:
    cmd = input('>>>:(输入q退出)').strip()
    if cmd == 'q':break
    if not cmd:continue
    client.send(cmd.encode('utf-8'))
    res = client.recv(1024)
    print(res.decode('utf-8'))

client.close()

三,协程

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

3.2,协程:
协程就是单线程实现并发

注意:协程是程序员编译出来的东西,操作系统里面只有进程

              和线程的概念(操作系统调度的是线程)

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

可以存在两种方式实现单线程,一种是简单的函数直接调用,这里就不具体演示了,主要举一下用yield实现线程在执行到一个代码之后切换和保存状态的

# 基于yield并发执行
import time
def func1():
    while True:
        print('func1')
        1000+1
        yield


def func2():
    g = func1()
    for i in range(1000):
        print('func2')
        time.sleep(10)
        i+1
        next(g)   #返回到func1函数执行

start = time.time()
func2()
stop = time.time()
print(stop-start)

四,gevent 模块

gevent是一个第三方库,在使用之前需要下载,可以通过gevent轻松的实现并发同步或者异步编程

注意:gevent在使用的时候,它只识别自己内部的io操作,要想让它识别所有的Io操作必须在程序前面导入

from gevent import monkey;monkey.patch_all()
from gevent import monkey;monkey.patch_all()
from gevent import spawn,joinall #pip3 install gevent(在cmd命令下面先下载此模块)
import time

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

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


start=time.time()
g1=spawn(play,'刘清正')
g2=spawn(eat,'刘清正')

# g1.join()
# g2.join()
# 上面两步合并成一步是下面是操作
joinall([g1,g2])
print('主',time.time()-start)

五,单线程下实现并发的套接字通讯

服务端

from gevent import monkey;monkey.patch_all()
from socket import *
from gevent import spawn

def comnicate(conn):
    while Ture:   #通讯循环
        try:
            data = conn.recv(1024)
            if not data:break
            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))
    serve.listen(backLog)

    while True:    #链接循环
        conn,client = 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,SOCKET_STREAM)
    client.connect(('127.0.0.1',8080))
    
    n = 0
    while True:
        msg = '%s sag hello %s' %(current_thread,n)
        n+=1
        client.send(msg.encode('utf-8'))
        data = client.recv(1024)
        print(data.decode('utf-8'))


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

 

你可能感兴趣的:(并发编程之协程(进程池与线程池))