概念
1.进程(同步)
进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。
上下文进程间的切换开销,比较大,但相对比较稳定安全
2.线程(同步)
线程是CPU调度和分派的基本单位。
线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
3.协程(异步)
协程是一种用户态的轻量级线程,协程的调度完全由用户控制
“协程就是你可以暂停执行的函数”。协程拥有自己的寄存器上下文和栈。
非抢占式多任务
协程与线程的区别:
一个线程可以多个协程,一个进程也可以单独拥有多个协程。
线程进程都是同步机制,而协程则是异步。
协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。
4)线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
5)协程并不是取代线程, 而且抽象于线程之上, 线程是被分割的CPU资源, 协程是组织好的代码流程, 协程需要线程来承载运行, 线程是协程的资源, 但协程不会直接使用线程, 协程直接利用的是执行器(Interceptor), 执行器可以关联任意线程或线程池, 可以使当前线程, UI线程, 或新建新程.。
6)线程是协程的资源。协程通过Interceptor来间接使用线程这个资源。
代码
greenlet
""" pip install greenlet """
from greenlet import greenlet import random import time
def Producer(): while True: item = random.randint(0, 99) print("pro生产了{}".format(item)) c.switch(item) time.sleep(1)
def Consumer(): while True: item = p.switch() print("消费了{}".format(item))
c = greenlet(Consumer) p = greenlet(Producer) c.switch()
""" # greenlet 的价值 价值一: 高性能的原生协程 价值二: 语义更加明确的显式切换 价值三: 直接将函数包装成协程,保持原有代码风格 """
|
gevent
gevent是一个基于协程的python网络库,在遇到IO阻塞时,程序会自动进行切换,可以让我们用同步的方式写异步IO代码。
""" pip install gevent
gevent,通过封装了 libev(基于epoll) 和 greenlet 两个库。 帮我们做好封装,允许我们以类似于线程的方式使用协程。 以至于我们几乎不用重写原来的代码就能充分利用 epoll 和 协程 威力 """
import gevent from gevent.queue import Queue
from gevent import monkey;monkey.patch_all() from gevent import monkey;monkey.patch_socket() import requests import gevent
def get_response(): print("start") requests.get("https://www.baidu.com") print("end")
tasks = [gevent.spawn(get_response) for i in range(20)] gevent.joinall(tasks)
import gevent
from gevent import monkey;monkey.patch_socket() import socket
server = socket.socket() server.bind(('127.0.0.1', 8888)) server.listen(1000)
def worker_coroutine(conn): while True: recv_data = conn.recv(1000) if recv_data: print(recv_data) conn.send(recv_data) else: conn.close() break
if __name__ == '__main__': while True: conn, remote_addr = server.accept() gevent.spawn(worker_coroutine, conn)
""" 问题一: 协程之间不是能通过switch通信嘛? 是的,由于 gevent 基于 greenlet,所以可以。
问题二: 那为什么还要考虑通信问题? 因为 gevent 不需要我们使用手动切换, 而是遇到阻塞就切换,因此我们不会去使用switch ! """
import gevent from gevent import monkey;monkey.patch_all() from gevent.queue import Queue import random
queue = Queue(3)
def Producer(): while True: item = random.randint(0, 99) print("生产了{}".format(item)) queue.put(item)
def Consumer(): while True: item = queue.get() print("消费了{}".format(item))
p = gevent.spawn(Producer, queue) c = gevent.spawn(Consumer, queue)
gevent.joinall([p, c])
|
Note:
import gevent from gevent import monkey monkey.patch_all()
|