Python笔记day41(并发)|协程、greenlet、gevent、非阻塞io模型、io多路复用

版权声明:转载请注明来源 https://blog.csdn.net/qq_34755081/article/details/82223711

1,协程

# 进程 启动多个进程 进程之间是由操作系统负责调用
# 线程 启动多个线程 真正被CPU执行的最小单位实际是线程
    # 开启一个线程 创建一个线程 寄存器 堆栈
    # 关闭一个线程
# 协程
    # 本质上是一个线程
    # 能够在多个任务之间切换来节省一些IO时间
    # 协程中任务之间的切换也消耗时间,但是开销要远远小于进程线程之间的切换
# 实现并发的手段
1)greenlet 真正的协程模块就是使用greenlet完成的切换
from greenlet import greenlet
def eat():
    print('eating start')
    g2.switch()
    print('eating end')
    g2.switch()

def play():
    print('playing start')
    g1.switch()
    print('playing end')
g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch()
运行结果    
eating start
playing start
eating end
playing end
2)使用monkey模块,monkey.patch_all()
# 进程和线程的任务切换右操作系统完成
# 协程任务之间的切换由程序(代码)完成,只有遇到协程模块能识别的IO操作的时候,程序才会进行任务切换,实现并发的效果
from gevent import monkey;monkey.patch_all()
import time
import gevent
import threading
def eat():
    print(threading.current_thread().getName())
    print(threading.current_thread())
    print('eating start')
    time.sleep(1)
    print('eating end')

def play():
    print(threading.current_thread().getName())
    print(threading.current_thread())
    print('playing start')
    time.sleep(1)
    print('playing end')

g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
g1.join()
g2.join()
运行结果
DummyThread-1
<_DummyThread(DummyThread-1, started daemon 1575671838024)>
eating start
DummyThread-2
<_DummyThread(DummyThread-2, started daemon 1575671838280)>
playing start
eating end
playing end
3)同步 和 异步

# 同步 和 异步
from gevent import monkey;monkey.patch_all()
import time
import gevent
def task(n):
    time.sleep(1)
    print(n)

def sync():
    for i in range(10):
        task(i)

def async():
    g_lst = []
    for i in range(10):
        g = gevent.spawn(task,i)
        g_lst.append(g)
    gevent.joinall(g_lst)  # for g in g_lst:g.join()

sync()
async()
4)爬虫例子
# 协程 : 能够在一个线程中实现并发效果的概念
    #    能够规避一些任务中的IO操作
    #    在任务的执行过程中,检测到IO就切换到其他任务

# 多线程 被弱化了
# 协程 在一个线程上 提高CPU 的利用率
# 协程相比于多线程的优势 切换的效率更快

# 爬虫的例子
# 请求过程中的IO等待
from gevent import monkey;monkey.patch_all()
import gevent
from urllib.request import urlopen    # 内置的模块
def get_url(url):
    response = urlopen(url)
    content = response.read().decode('utf-8')
    return len(content)

g1 = gevent.spawn(get_url,'http://www.baidu.com')
g2 = gevent.spawn(get_url,'http://www.sogou.com')
g3 = gevent.spawn(get_url,'http://www.taobao.com')
g4 = gevent.spawn(get_url,'http://www.hao123.com')
g5 = gevent.spawn(get_url,'http://www.cnblogs.com')
gevent.joinall([g1,g2,g3,g4,g5])
print(g1.value)
print(g2.value)
print(g3.value)
print(g4.value)
print(g5.value)


2,协程完成socket

server端:

from gevent import monkey;monkey.patch_all()
import socket
import gevent
def talk(conn):
    conn.send(b'hello')
    print(conn.recv(1024).decode('utf-8'))
    conn.close()

sk = socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
while True:
    conn,addr = sk.accept()
    gevent.spawn(talk,conn)
sk.close()

client端:

import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8080))
print(sk.recv(1024))
msg = input('>>>').encode('utf-8')
sk.send(msg)
sk.close()

3,io模型

# 同步 提交一个任务之后要等待这个任务执行完毕
# 异步 只管提交任务,不等待这个任务执行完毕就可以做其他事情
# 阻塞 recv recvfrom accept
# 非阻塞

# 阻塞   线程   运行状态 --> 阻塞状态 --> 就绪
# 非阻塞

# IO多路复用
    # select机制  Windows  linux  都是操作系统轮询每一个被监听的项,看是否有读操作
    # poll机制    linux          它可以监听的对象比select机制可以监听的多
                                 # 随着监听项的增多,导致效率降低
    # epoll机制   linux
1)非阻塞io模型,socket

server端:

import socket
sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.setblocking(False)
sk.listen()
conn_l = []
del_conn = []
while True:
    try:
        conn,addr = sk.accept()  #不阻塞,但是没人连我会报错
        print('建立连接了:',addr)
        conn_l.append(conn)
    except BlockingIOError:
        for con in conn_l:
            try:
                msg = con.recv(1024)  # 非阻塞,如果没有数据就报错
                if msg == b'':
                    del_conn.append(con)
                    continue
                print(msg)
                con.send(b'byebye')
            except BlockingIOError:pass
        for con in del_conn:
            con.close()
            conn_l.remove(con)
        del_conn.clear()
# while True : 10000   500  501

client端:

import time
import socket
import threading
def func():
    sk = socket.socket()
    sk.connect(('127.0.0.1',9000))
    sk.send(b'hello')
    time.sleep(1)
    print(sk.recv(1024))
    sk.close()

for i in range(2):
    threading.Thread(target=func).start()
2)io多路复用,socket,使用select模块

server端:

import select
import socket

sk = socket.socket()
sk.bind(('127.0.0.1',8000))
sk.setblocking(False)
sk.listen()

read_lst = [sk]
while True:   # [sk,conn]
    r_lst,w_lst,x_lst = select.select(read_lst,[],[])
    for i in r_lst:
        if i is sk:
            conn,addr = i.accept()
            read_lst.append(conn)
        else:
            ret = i.recv(1024)
            if ret == b'':
                i.close()
                read_lst.remove(i)
                continue
            print(ret)
            i.send(b'goodbye!')

client端:

import time
import socket
import threading
def func():
    sk = socket.socket()
    sk.connect(('127.0.0.1',8000))
    sk.send(b'hello')
    time.sleep(3)
    print(sk.recv(1024))
    sk.close()

for i in range(20):
    threading.Thread(target=func).start()

猜你喜欢

转载自blog.csdn.net/qq_34755081/article/details/82223711