Python 多道技术以及进程、线程和协程

多道技术

并发:看起来像同时运行

并行:真正意义上的同时运行,并行肯定是并发

空间的复用与时间复用

  • 空间复用

多个程序用一套计算机硬件

  • 时间复用

程序切换节省时间

'''
切换(cup)分为两种情况
1.当一个程序遇到IO操作的时候,操作系统会剥夺该程序的CPU执行权限
	作用:提高CPU的利用率,并且不影响程序的执行效率

2.当一个程序长时间占用CPU的时候,操作吸引也会剥夺该程序的CPU执行权限
	弊端:降低程序的执行效率(原本时间+切换时间)
'''

 

进程

程序与进程的区别

程序是一堆代码,保存在硬盘上,是“死”的。

进程表示的是程序正在执行的过程,是”活“的。

 

进程的调度

  • 先来先服务调度算法
  • 短作业优先调度算法
  • 时间片轮转法+多级反馈队列

进程三状态图

进程三状态图

 

两对重要概念

同步和异步

描述的是任务的提交方式

  • 同步:任务提交后,原地等待任务的返回结果,等待的过程不做任何事情(程序层面上表现出来感觉就是卡住了)
  • 异步:任务提交之后,不原地等待任务的返回结果,直接去做其他事情。任务的返回结果会有一个异步回调机制自动处理。

阻塞和非阻塞

描述的程序的运行状态

  • 阻塞:阻塞态
  • 非阻塞:非阻塞态

理想状态:代码永远处于就绪态和运行态之间切换

上述概念的组合:最高效的一种组合就是异步非阻塞

 

开启进程的两种方式

代码开启进程和线程的方式是一样的。

第一种方式

# 第一种方式		类实例化产生对象

from multiprocessing import Process
import time

def task(name):
    print('%s is running'%name)
    time.sleep(3)
    print('%s is over'%name)

if __name__ == '__main__':
    # 1.创建一个对象
    p = Process(target=task,args=('hp',))
    # 容器类型哪怕里面只有一个元素都要用逗号隔开
    # 2.开启进程
    p.start()   #告诉操作系统创建一个进程 异步
    print('***********')

'''
windows操作系统下 创建进程一定要在main内创建
因为windows下创建进程类似于模块导入的方式
会从上往下依次执行代码

在linux中,直接将代码完整拷贝一份
'''

第二种方式

# 第二种方式  类的继承

from multiprocessing import Process
import time

class MyProcess(Process):
    def run(self):
        print('hello bf girl')
        time.sleep(1)
        print('get out')

if __name__ == '__main__':
    p = MyProcess()
    p.start()
    print('***********')

总结

'''
创建进程就是在内存中申请一块内存空间将需要运行的代码丢进去
一个进程对应在内存中就是一块内存空间
多个进程对应在内存中就多快独立的内存空间
进程与进程之间数据默认情况下就是无法之间交互,如果想交互可以借助第三方工具、模块
'''

 

join方法

join是让主进程等待子进程代码运行结束之后,再继续运行。不影响其他子进程运行。

from multiprocessing import Process
import time

def task(name,n):
    print('%s is running'%name)
    time.sleep(n)
    print('%s is over'%name)

if __name__ == '__main__':
    start_time = time.time()
    # # 1.创建一个对象
    # p1 = Process(target=task,args=('hp',1))
    # p2 = Process(target=task, args=('hp',2))
    # p3 = Process(target=task, args=('hp',3))
    # # 2.开启进程
    # p1.start()   #告诉操作系统创建一个进程 异步
    # p2.start()
    # p3.start()
    # p1.join()    # 主进程等待子进程p运行结束之后再继续执行
    # p2.join()
    # p3.join()

    p_list = []
    for i in range(1,4):
        p = Process(target=task, args=('hp', i))
        p.start()
        p_list.append(p)    #先全部开启进程
        #p.join()   # 6秒多
    for p in p_list:    #通过for循环同时运行进程
        p.join()    # 3秒多    

    print('***********',time.time()-start_time)

 

进程数据相互隔离(默认情况下)

from multiprocessing import Process

money = 100

def tack():
    global money
    money = 666
    print('子进程:%s'%money)

if __name__ == '__main__':
    p = Process(target=tack)
    p.start()
    p.join()
    print('主进程:%s'%money)


#输出结果
子进程:666
主进程:100

 

进程对象以及其他方法

'''
一台计算机上面运行着很多进程,那么计算机是如何区分并管理这些进程服务端的呢?
计算机会给每一个运行的进程分配一个PID号

如何查看
	windows电脑 cmd输入tasklist即可查看
		tasklist |findstr PID查看具体的进程
	mac电脑 进入终端ps aux
		ps aux|grep PID查看具体进程
'''

from multiprocessing import Process,current_process
current_process().pid	#查看当前的进程号

import os
os.getpid()		#查看当前的进程号
os.getppid()	#查看当前进程的父进程号

 

守护进程

父进程结束守护进程也结束。

from multiprocessing import Process
import time


def tack(name):
    print('%s总管正在存活' % name)
    time.sleep(3)
    print('%s总管正在死亡' % name)

if __name__ == '__main__':
    p = Process(target=tack,args=('Hp',))
    p.daemon = True  # 将进程p设置成守护进程,一定要放在p.start上面才有效,否则直接报错
    p.start()
    print('皇帝寿终正寝')

# 输出结果:
皇帝寿终正寝

 

互斥锁

多个进程操作同一份数据的时候,会出现数据错乱的问题

解决方式:加锁处理

加锁处理:将并发变成串行,牺牲效率但是保证数据的安全

from multiprocessing import Process, Lock # 互斥锁
import json
import time
import random

# 查票
def search(i):
    # 文件操作读取票数
    with open('data','r',encoding='utf-8') as f:
        dic = json.load(f)	# json.load()从文件中读取json字符串
    print('用户%s查询余票:%s' %(i,dic.get('ticket_num')))
    # 字典取值不要用[]的形式,推荐使用get

# 买票  1.查票   2.买票
def buy(i):
    # 先查票
    with open('data','r',encoding='utf-8') as f:
        dic = json.load(f)
    # 模拟网络延迟
    time.sleep(random.randint(1,3))
    # 判断当前是否有票
    if dic.get('ticket_num') > 0:
        # 修改数据库买票
        dic['ticket_num'] -= 1
        # 写入数据库
        with open('data','w',encoding='utf-8') as f:
            json.dump(dic,f)	# json.dump()是通过文件的形式进行处理
        print('用户%s买票成功'%i)
    else:
        print('用户%s购票失败'%i)

# 整合上面两个函数
def run(i, mutex):
    search(i)
    # 给买票环节加锁处理
    # 抢锁
    mutex.acquire()
    buy(i)
    # 释放锁
    mutex.release()

if __name__ == '__main__':
    # 在主进程中生成一把锁 让所有子进程抢,谁先抢到谁就先买
    mutex = Lock()
    for i in range(1,10):
        p = Process(target=run,args=(i, mutex))
        p.start()

'''
扩展:行锁	表锁
注意:
	1.锁不要轻易的使用,容易造成死锁现象(写代码的时候一般不会用到,都是内部封装好的)
	2.锁只在处理数据的部分加来保证数据安全(只在争抢数据的环节加锁处理)
'''

 

进程间通信

队列Queue模块

管道:subprocess

stdin stdout stderr

队列:先进先出,管道+锁

堆栈:先进后出

import queue

# 创建队列
q = queue.Queue(5)   # 括号内可以传数字  表示生成队列最大可以同时存放的数据量

# 往队列中存放数据
q.put(111)
q.put(222)
q.put(333)
# print(q.full()) 	# 判断当前队列是否满了
# print(q.empty())  # 判断当前队列是否空了
q.put(444)
q.put(555)
# print(q.full())
# q.put(666)  		# 当队列数据放满之后 如果还有数据会阻塞 直到有位置让出来



# 去队列中取数据
v1 = q.get()
v2 = q.get()
v3 = q.get()
v4 = q.get()
v5 = q.get()
# v6 = q.get_nowait()     # 没有数据直接报错 queue.Empty
# v6 = q.get(timeout=3)   # 没有数据原地等待3秒之后再报错	queue.Empty
# v6 = q.get()    		  # 队列中如果没有数据 get方法会原地阻塞
try:
    v6 = q.get(timeout=3)
    print(v6)
except Exception as e:
    print('没有了')
# print(v1,v2,v3,v4,v5,)


'''
q.full()
q.empty()
q.get_nowait()
在多进程的情况下是不精确的
'''

 

IPC机制

IPC机制的含义是进程间通信或者跨进程通信,是指两个进程之间进行数据交换。

from multiprocessing import Queue, Process

'''
研究思路
    1.主进程跟子进程借助于队列通信
    2.子进程跟子进程借助于队列通信
'''
def producer(q):
    q.put('hello word')		# 向队列存入	hello word
    # print('hello big baby')

def consumer(q):
    print(q.get())		#向队列中取出数据

if __name__ == '__main__':
    q = Queue()
    p = Process(target=producer,args=(q,))
    p1 = Process(target=consumer,args=(q,))
    p.start()
    p1.start()
    # print(q.get())

 

生产者消费者模型

生产者:生产/制作东西

消费者:消费/处理东西

该模型除了上述两个之外还需要一个媒介

生产者和消费者之间不是直接做交互的,而是借助于媒介做交互

生产者 + 消息队列 +消费者

from multiprocessing import Queue, Process, JoinableQueue
import time
import random

def property(name, food, q):
    for i in range(4):
        data = '%s生产了%s%s'% (name, food, i)
        # 模拟延迟
        time.sleep(random.randint(1,3))
        print(data)
        # 将数据放入队列中
        q.put(data)



def consumer(name,q):
    # 全部处理
    while True:
        food = q.get()
        # 判断当前是否有结束标记
        # if food is None:break
        time.sleep(random.randint(1,3))
        print('%s吃了%s' % (name,food))
        q.task_done()   # 告诉队列已经从里面取出一个数据并且处理完毕



if __name__ == '__main__':
    # q = Queue()
    q = JoinableQueue()
    p1 = Process(target=property,args=('大厨', '凉拌青瓜', q))
    p2 = Process(target=property,args=('Hp', '泰式鸡爪', q))
    c1 = Process(target=consumer,args=('sj',q))
    p1.start()
    p2.start()
    
    # 将消费者设置成守护进程
    c1.daemon = True
    c1.start()

    p1.join()
    p2.join()


    # 等生产者生产完毕之后 往队列中添加特定的结束符号
    # q.put(None) # 使用Queue()的时候,在所有生产者生产的数据的末尾加上结束符号
    q.join() # 等待队列中所有的数据被取完再往下执行


    '''
    JoinableQueue 每当你往该队列中存入数据的时候,内部会有一个计数器+1
    每当你调用task_done的时候 计数器-1
    q.join()    当计数器为0的时候才往下运行
    '''
    # 只有q.join执行完毕 说明消费者已经处理完数据,消费者没必要存在了

 

线程

什么是线程

'''
进程:资源单位(起一个进程仅仅只是在内存空间中开辟一块独立的空间)
线程:执行单位(真正被CPU执行的其实是进程里面的线程,线程指的是代码的执行过程,执行代码中所需要使用到的资源都找所在的进程索要)

每一个进程肯定带一个线程

进程和线程都是虚拟单位,只是为了更加方便的描述问题
'''

为何要有线程

'''
开设进程
	1.申请内存空间	耗资源
	2.“拷贝代码”	 耗资源
开线程
	一个进程内可以开设多个线程,在用一个进程内开设多个线程无需再次申请内存空间操作

总结:
	开设线程的开销远远小于进程的开销
	同一个进程下的多个线程都是共享的
'''

 

如何使用

开启线程的两种方式

# 第一种方式		类的实例化对象
from multiprocessing import Process
from threading import Thread
import time

def task(name):
    print('%s is running' % name)
    time.sleep(1)
    print('%s is over' % name)


'''
开启线程不需要在main下面书写代码,直接书写就可以
但是还是习惯性将启动命令写在main下面
'''

t = Thread(target=task,args=('hp',))
# p = Process(target=task,args=('sj',))
# p.start()
t.start()   # 创建线程的开销非常小 几乎代码一执行线程就已经创建了
print('主')
# 第二种方式		类的继承
from threading import Thread
import time

class MyThread(Thread):
    def __init__(self, name):
    # 重写别的方法,调用父类的方法
        super().__init__()
        self.name = name

    def run(self):
        print('%s is running' % self.name)
        time.sleep(1)
        print('%s is over'% self.name)

if __name__ == '__main__':
    t = MyThread('hp')
    t.start()
    print('主线程')

 

TCP服务端实现并发效果

import socket
from threading import Thread
from multiprocessing import Process

'''
服务端
    1.固定的IP和端口
    2.24小时不间断提供服务
    3.支持并发
'''

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)   #括号内不加参数默认TCP协议
server.bind(('127.0.0.1',8080)) #绑定连接
server.listen(5)    #指的是半连接池的大小

# 将服务代码单独封装成一个函数
def talk(conn):
    # 通信循环
    while True:
        try:
            data = conn.recv(1024)
            # 针对mac linux 客户端断开链接
            if len(data) == 0: break
            print(data.decode('utf-8'))
            conn.send(data.upper())
        except Exception as e:
            print(e)
            break

    conn.close()


# 链接循环
while True:
    conn, addr = server.accept()    #接客
    # 其他人服务客户
    t = Thread(target=talk,args=(conn,))
    t.start()

 

线程对象的join方法

from threading import Thread
import time


def task(name):
    print('%s is running' % name)
    time.sleep(1)
    print('%s is over' % name)

if __name__ == '__main__':
    t = Thread(target=task,args=(('hp',)))
    t.start()
    t.join()    #主线程等待子线程结束再执行
    print('主线程')

 

同一个进程下的多个线程的数据是共享的

from threading import Thread
import time

money = 100

def task():
    global money
    money = 666
    print(money)

if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    t.join()
    print(money)

 

线程对象及属性方法

from threading import Thread , active_count , current_thread
import os,time

def task(n):
    print('hello world',current_thread().name)  # current_thread().name获取当前线程的名字,子线程
    time.sleep(n)

if __name__ == '__main__':
    t = Thread(target=task,args=(1,))
    t1 = Thread(target=task,args=(2,))
    t.start()
    t1.start()
    t.join()
    print('主',active_count())   # active_count()统计当前正在活跃的线程数
    # print('主',os.getpid())	# 获取当前进程的pid号
    # print('主', current_thread().name)   # 获取当前主线程名字

 

守护线程

from threading import Thread
import time


def task(name):
    print('%s is running' % name)
    time.sleep(1)
    print('%s is over' % name)


if __name__ == '__main__':
    t = Thread(target=task,args=('hp',))
    t.daemon = True
    t.start()
    print('主线程')


'''
主线程运行结束之后不会立刻结束 会等待所有其他非守护线程结束才会结束
'''

# 例子
from threading import Thread
import time

def foo():
    print(123)
    time.sleep(1)
    print('end123')

def func():
    print(456)
    time.sleep(3)
    print('end456')


if __name__ == '__main__':
    t1 = Thread(target=foo)
    t2 = Thread(target=func)
    t1.daemon = True
    t1.start()
    t2.start()
    print('主....')

 

线程互斥锁

from threading import Thread, Lock
import time

money = 100
mutex = Lock()

def task():
    global money
    mutex.acquire()
    tmp = money
    time.sleep(0.1)
    money = tmp - 1
    mutex.release()


if __name__ == '__main__':
    t_list = []
    for i in range(100):
        t = Thread(target=task)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    print(money)

 

GIL全局解析器锁

python解析器有很多版本,其中包括:Cpython,Jpython,Pypypython。普遍使用的都是Cpython解析器。

  • CIL不是python的特点而是Cpython解析器的特点
  • GIL是保证解析器级别的数据安全
  • GIL会导致同一个进程下的多个线程的无法同时执行即无法利用多核优势
  • 针对不同的数据还是需要加不同的锁处理

在Cpython解析器中GIL是一把互斥锁,用来阻止同一个进程下的多个线程的同时执行。简单来说,就是进程下的多个线程无法利用多核优势。

原因:Cpython中的 内存管理(垃圾回收机制)不是线程安全的

内存管理(垃圾回收机制)

  1. 应用计数
  2. 标记清楚
  3. 分代回收

 

GIL与普通互斥锁的区别

from threading import Thread, Lock
import time

mutex = Lock()
money = 100

def task():
    global money
    mutex.acquire()
    tmp = money
    time.sleep(0.1)
    money = tmp - 1
    mutex.release()

if __name__ == '__main__':
    t_list = []
    for i in range(100):
        t = Thread(target=task)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    print(money)

'''
100个线程起来之后 先抢GIL
进入io GIL自动释放 但是手上还有一把自己的互斥锁
其他线程抢到GIL但是没有抢到互斥锁
最终GIL还是回到你的手上,按你的照步骤操作数据
'''

 

多进程与多线程比较

同一程序的多线程

'''
多线程是否有用看具体情况:
单核:四个任务(IO密集型\计算密集型)
多核:四个任务(IO密集型\计算密集型)
'''
#计算密集型	每个任务都需要10s
单个:
	多进程:额外消耗资源
	多线程:节省开销
多核:
	多进程:总耗时 10+
    多线程:总耗时	40+
# IO密集型
多核:
	多进程:相对浪费资源
    多线程:更加节省资源

验证

# 计算密集型
from multiprocessing import Process
from threading import Thread
import os, time

def work():
    res = 0
    for i in range(100000):
        res *= i
if __name__ == '__main__':
    l = []
    print(os.cpu_count())   #获取当前计算机CPU个数
    start_time = time.time()
    for i in range(8):
        p = Process(target=work)    # 0.4827229976654053
        t = Thread(target=work)     # 0.04627394676208496
        p.start()
        l.append(p)
        # t.start()
        # l.append(t)
    for p in l:
        p.join()
    print(time.time()-start_time)


# IO密集型
from multiprocessing import Process
from threading import Thread
import os, time

def work():
    time.sleep(2)


if __name__ == '__main__':
    l = []
    print(os.cpu_count())   #获取当前计算机CPU个数
    start_time = time.time()
    for i in range(400):
        p = Process(target=work)    # 22.63789129257202
        t = Thread(target=work)       # 2.0869174003601074
        # p.start()
        # l.append(p)
        t.start()
        l.append(t)
    for p in l:
        p.join()
    print(time.time()-start_time)

总结

'''
多进程和多线程都有各自的优势
写项目的时候都是多进程下面再开多线程
这样可以利用多核优势也可以节省资源消耗
'''

 

进程池与线程池

无论开设进程还是开设线程都需要消耗资源,只不过开设线程的消耗比开设进程的消耗小。

我们不可能做到无限制的开设进程和线程,因为计算机的硬件资源跟不上。硬件的开发速度远远跟不上软件。

因此在保证计算机硬件能够正常工作的情况下最大限度的利用它。

 

池的概念

池是用来保证计算机硬件安全的情况下做大限度的利用计算机。

它降低了程序的运行效率但是保证计算机硬件的安全,从而让程序能够运行。

基本使用

线程池

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time

# 括号内可以传数字,不传的话默认会开设当前计算机cpu个数的5倍的线程
pool = ThreadPoolExecutor(5)    # 池子内固定5个线程
'''
池子造出来后,里面会固定存在5个线程
这5个线程不会出现重复创建和销毁的过程

池子的使用:
    将需要运行的任务往池子中提交,池子会自动服务
'''

def task(n):
    print(n)
    time.sleep(2)

'''
任务提交方式:
    同步:提交任务之后原地等待任务返回结果 期间不做任何事情
    异步:提价任务之后不等待任务返回结果,继续往下执行
    	任务结果通过异步回调机制
'''
# pool.submit(task, 1)    # 往池子中提交任务  异步提交
# print('主')

t_list = []
for i in range(20):
    res = pool.submit(task, i)  # <Future at 0x1c3fbd4c048 state=running>
    # print(res.result()) # result方法
    t_list.append(res)
#等待线程池中所有任务执行完毕之后再往下执行
pool.shutdown() #关闭线程池  等待线程池中所有的任务运行完毕
for t in t_list:
    print('>>>:',t.result())    # 有序的

'''
程序由并发变成串行
res.result()拿到的是异步提交的返回结果
'''

进程池

# 进程池
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time, os

pool = ProcessPoolExecutor()
# 括号内可以传数字,不传的话默认会开设当前计算机cpu个数进程

'''
池子造出来之后,里面固定几个进程
这几个进程不会出现重复创建和销毁的过程
'''

def task(n):
    print(n,os.getpid())
    time.sleep(2)
    return n**n

def call_back(n):
    print('call_back:',n.result())


if __name__ == '__main__':
    t_list = []
    for i in range(20):
        # res = pool.submit(task, i)  # <Future at 0x1c3fbd4c048 state=running>
        res = pool.submit(task, i).add_done_callback(call_back)	#异步回调结果
        # print(res.result()) # result方法
        # t_list.append(res)

    # pool.shutdown() #关闭进程池  等待进程程池中所有的任务运行完毕
    #     # for t in t_list:
    #     #     print('>>>:',t.result())    # 有序的

 

协程

概念

进程:资源单位

线程:执行单位

协程:程序员自己想出来的,根本不存在

单线程下实现并发效果,程序员在代码层面上检测所有IO操作,一旦遇上IO,在代码级别上完成切换,这样给CPU感觉程序一直在运行,没有IO从而提升程序的运行效率。

代码层面

切换+保存状态

切换

切换不一定是提升效率,也可能降低效率。IO切换提升效率,没有IO切换则降低效率。

保存状态

保存上一次执行的状态,下一次接着上一次的操作继续往下执行

yield

yield关键字

import time

# def func1():
#     for i in range(1000000):
#         i + 1
#
# def func2():
#     for i in range(1000000):
#         i + 1
# start_time = time.time()
# func1()
# func2()
# print(time.time()-start_time)


# 切换 + yield 	时间长
def func1():
    while True:
        1000000 + 1
        yield

def func2():
    g = func1() #   先初始化出生成器
    for i in range(1000000):
        i + 1
        next(g)

start_time = time.time()
func2()
print(time.time() - start_time)

 

gevent模块

实现同步并发或异步编程,遇到IO阻塞时会自动切换任务。

安装

pip install gevent

使用

from gevent import monkey;monkey.patch_all()   # 猴子补丁
from gevent import spawn
import time
'''
gevent模块本身无法检测常见的一些io操作
在使用的时候需要额外的导入一句话
from gevent import monkey   # 猴子补丁
monkey.patch_all()
由于上面两句话在使用gevent模块的时候肯定要导入的
所以还支持简写
from gevent import monkey;monkey.patch_all()
'''

def heng():
    print('哼')
    time.sleep(2)
    print('哼')

def ha():
    print('哈')
    time.sleep(3)
    print('哈')

start_time = time.time()
g1 = spawn(heng)
g2 = spawn(ha)
g1.join()
g2.join()   # 等待被检测的任务执行完毕  再往后继续执行
print(time.time() - start_time) # 3.0231306552886963

# heng()
# ha()
# print(time.time() - start_time) # 5.000835418701172

 

asyncio

在python3.4版本后支持使用

import asyncio

@asyncio.coroutine
def func1():
    print(1)
    # 网络IO请求:下载图片
    yield from asyncio.sleep(2) # 遇到ID操作,自动切换到tasks中的其他任务
    print(2)
    
@asyncio.coroutine
def fun2():
    print(3)
    # 网络IO请求:下载图片
    yield from asyncio.sleep(2)
    print(4)
    
tasks = [
    asyncio.ensure_future(func1()),
    asyncio.ensure_future(func2()),
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

注意:遇到IO自动切换

 

async & await关键字

在python3.5版本之后才能使用

import asyncio

async def func1():
    print(1)
    # 网络IO请求:下载图片
    await asyncio.sleep(2) # 遇到ID操作,自动切换到tasks中的其他任务
    print(2)
    
async def fun2():
    print(3)
    # 网络IO请求:下载图片
    await asyncio.sleep(2)
    print(4)
    
tasks = [
    asyncio.ensure_future(func1()),
    asyncio.ensure_future(func2()),
]

# 生成获取一个循环事件
loop = asyncio.get_event_loop()
# 将任务放到任务列表
loop.run_until_complete(asyncio.wait(tasks))

# asyncio.run(asyncio.wait(tasks)) # python3.7使用该方法创建

Task对象

在事件循环中添加多个任务。

Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入到事件循环中等待被调度执行。除了使用asyncio.create_task()函数创建,还可以使用低层级的loop.create_task()ensure_future()函数。不建议手动实例化创建Task对象。

注意:asyncio.create_task()函数在Python3.7中被加入的。在Python3.7之前,使用asyncio.ensure_future()函数。

 

示例1:

import asyncio

async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return "返回值"

async def main():
    print("main函数开始")
    
    # 创建Task对象,将当前执行的func函数添加到事件循环中
    # name给协程对象起名字
    task_list = [
        asyncio.create_task(func(),name='n1'),
    	asyncio.create_task(func(),name='n2')
    ]
    print("main结束")
    
    # 当执行某协程遇到IO操作时,会自动切换到其他任务
    # done获取的是执行完返回的结果集合,可以通过name获取是哪个协程的结
    # timeout等待时间,None,表示全部协程执行完成
    done,pending = await asyncio.wait(task_list,timeout=None)
    print(done)

asyncio.run(main())

示例2:

import asyncio

async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return "返回值"


task_list = [
    func(),
    func(),
]

done,pending = asyncio.run(asyncio.wait(task_list))
print(done)

 

Future对象

Task继承Future,Task对象内部await结果的处理基于Future对象来的。

示例:

async def main():
    # 获取当前事件循环
    loop = asyncio.get_running_loop()
    
    # 创建一个任务(Future对象),这个任务什么都不做
    fut = loop.create_future()
    
    # 等待任务最终结构钢(Future对象),没有结果则会一直等下去
    await fut
asyncio.run(main())

 

 

 

协程实现TCP服务端并发

from gevent import monkey;monkey.patch_all()
from gevent import spawn
from threading import Thread
import socket


def communication(conn):
    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0:break
            conn.send(data.upper())
        except Exception as e:
            print(e)
            break
    conn.close()

def server(ip, port):
    server = socket.socket()
    server.bind((ip, port))
    server.listen(5)
    while True:
        conn, addr = server.accept()
        spawn(communication,conn)

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

 

异步redis

pip install aioredis

示例:

import asyncio 
import aioredis

async def execute(address,password):
    print("开始执行",address)
    # 网络IO操作,创建redis连接
    redis = await aioredis.create_redis(address,password=password)
    
    # 网络IO操作:redis中设置哈希值car,内部在设三个键值对,即:redis={car:{key1:1,key2:2,key3:3}}
    await redis.hmset_dict('car',encoding='utf-8')
    print(result)
    
    redis.close()
    # 网络IO操作:关闭redis连接
    await redis.wait_closed()
    
    print("结束",address)
asyncio.run(execute('redis:127.0.0.1:6379',"密码"))

 

异步MySql

pip install aiomysql

示例1:

import asyncio 
import aiomysql

async def execute():
    # 网络IO操作,创建mysql连接
    conn = await aiomysql.connect(host='127.0.0.1',post=3306,user='root',password='123',db='mysql')
    
    # 网络IO操作:创建CURSOR
    cur = await conn.cursor()
    
    # 网络IO操作:执行SQL
    await cur.execute("SELECT HOST,User FORM user")
    
    # 网络IO操作:获取SQL结果
    result = await cur.fetchall()
    print(result)
    
    # 网络IO操作:关闭链接
    await cur.close()
    conn.close()
    
asyncio.run(execute())

 

posted @ 2021-03-09 15:35  Hp_mzx  阅读(92)  评论(0)    收藏  举报