协程的概念、I/O模型

GIL全局解释器锁

    什么是GIL?
        GIL本质就是一个把互斥锁,是将多个并发的线程对共享数据的修改变成“串行”(并非真的串行,详情:                      http://www.cnblogs.com/linhaifeng/articles/7449853.html,第三节GIL和Lock)
    为何有?
    Cpython解释器的垃圾回收机制不是线程安全的

    如何用?
    有GIL的存在,导致同一个进程内的多个线程同一时刻只能有一个运行,即同一进程的多个线程 无法实现运行===》无法利用多核优势
    但是可以实现并发的效果
    什么是多核优势?
    多核即多个CPU,多个CPU带来的优势是计算性能的提升,所以

    I/O密集型:
        多线程
    计算密集型:
        多进程

GIL vs 自定义互斥锁
      GIL相当于执行权限,意思是在一个进程内的多个线程想要执行,必须先抢GIL,这把锁的特点是,当一个线程被剥夺走cpu的执行权限的同时会被解释器强行释放GIL

2、进程池与线程池?
    什么是?
        池:池用来限制进程/线程个数的一种机制
    为何用?
        当并发的任务数远大于计算机的承受能力,应该用池的概念
        将并发的进程数/线程数控制在计算机能承受的数目

    如何用?

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
def task(n):
    return n**2
def func(future):
    print(future.result())

if __name__ == '__main__':
    p=ProcessPoolExecutor(4)
    p.submit(task,10).add_done_callback(func) # add_done_callback(fn):回调函数,func会在p.submit(task,10))有返回值时立刻触发,并且将p.submit(task,10)当作参数传给func

    p.shutdown(wait=True)
    # pool.close()
    # pool.join()
    print('主')

提交任务的两种方式:

    1、同步:提交完任务后就在原地等待,直到任务运行完毕并且拿到返回值后,才运行下一行代码
    2、异步:提交完任务(绑定一个回调函数)后不原地等待,直接运行下一行代码,等到任务运行有返回值自动触发回调的函数的运行

    程序的运行状态(阻塞,非阻塞)

        1、阻塞
            I/O阻塞
        2、非阻塞
            运行
            就绪

协程

1、协程

单线程实现并发
在应用程序里控制多个任务的切换+保存状态
优点:

应用程序级别速度要远远高于操作系统的切换

缺点:

多个任务一旦有一个阻塞没有切,整个线程都阻塞在原地,该线程内的其他的任务都不能执行了
一旦引入协程,就需要检测单线程下所有的I/O行为,实现遇到I/O就切换,少一个都不行,因为一旦一个任务阻塞了,整个线程就阻塞了,其他的任务即使是可以计算,但是也无法运行了

2、协程的目的:

想要在单线程下实现并发
并发指的是多个任务看起来是同时运行的
并发=切换+保存状态

# 串行执行

import time

def func1():
    for i in range(10000000)
        i+1
def func2():
    for i in range(10000000):
        i+1

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

'''
结果:
1.4379246234893799
'''
# 基于yield并发执行

import time
def func1():
    while True:
        print('func1')
        yield

def func2():
    g=func1()
    for i in range(10000000):
        print('func2')
        i+1
        time.sleep(3)
        next(g)

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

'''
结果:
func2
--等待3秒--
func1
func2
--等待3秒--
func1
func2
--等待3秒--
func1
func2
--等待3秒--
func1
func2
--等待3秒--
func1
......
'''

 使用gevent模块:Gevent是第三方库,需要提前安装在python中才能使用,此模块可以通过gevent实现并发同步或者异步编程。
gevent直接使用不能识别time.sleep,需要通过打补丁:monkey.path_all(),并且放在被打补丁者(import.time)之前生效,简写成下方首先代码即可。

安装方法:pip3 install gevent 

from gevent import monkey,spawn;monkey.patch_all()    
# 注意上下代码顺序不可颠倒过来
import time

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

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

start=time.time()
g1=spawn(eat,'egon')
g2=spawn(play,'zmy')

g1.join()    
g2.join()    #此处两个join可以有简写方式:gevent.joinall([g1,g2])
print(time.time() - start)
print(g1)
print(g2)

'''
结果:
egon eat 1
zmy play 1
zmy play 2
egon eat 2
3.0080697536468506
<Greenlet "Greenlet-0" at 0x3329ad0: _run>
<Greenlet "Greenlet-1" at 0x3329b58: _run>
'''
from gevent import monkey,spawn;monkey.patch_all()
from threading import current_thread
import time

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

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

g1=spawn(eat,)
g2=spawn(play,)

print(current_thread().name)
g1.join()
g2.join()

'''
结果:
MainThread
DummyThread-1 eat 1    #DummyThread为虚拟线程
DummyThread-2 play 1
DummyThread-2 play 2
DummyThread-1 eat 2
'''
# 并发的套接字通信

# 服务端:
from gevent import spawn,monkey;monkey.patch_all()
from socket import *
from threading import Thread

def talk(conn):
    while True:
        try:
            data=conn.recv(1024)
            if len(data) == 0:break
            conn.send(data.upper())
        except ConnectionResetError:
            break
    conn.close()

def server(ip,port,backlog=5):    #传入ip,port,backlog=5(默认为5)
    server = socket(AF_INET, SOCK_STREAM)
    server.bind((ip, port))
    server.listen(backlog)

    print('starting...')
    while True:
        conn, addr = server.accept()
        spawn(talk, conn,)

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

# 客户端:
from threading import Thread,current_thread
from socket import *
import os

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

    while True:
        msg='%s say hello' %current_thread().name
        client.send(msg.encode('utf-8'))
        data=client.recv(1024)
        print(data.decode('utf-8'))

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

I/O模型

网络I/O:

recvfrom:
        wait data:等待客户端产生数据————》客户端OS————》网络————》服务端操作系统缓存
        copy data:由本地操作系统缓存中的数据拷贝到应用程序的内存中

非阻塞I/O模型

# 服务端:
from socket import *
import time

server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
server.setblocking(False)

conn_l=[]
while True:
    try:
        print('总连接数[%s]' % len(conn_l))
        conn,addr=server.accept()
        conn_l.append(conn)
    except BlockingIOError:
        del_l=[]
        for conn in conn_l:
            try:
                data=conn.recv(1024)
                if len(data) == 0:
                    del_l.append(conn)
                    continue
                conn.send(data.upper())
            except BlockingIOError:
                pass
            except ConnectionResetError:
               del_l.append(conn)

        for conn in del_l:
            conn_l.remove(conn)

# 客户端:
from socket import *
import os

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

while True:
    msg='%s say hello' %os.getpid()
    client.send(msg.encode('utf-8'))
    data=client.recv(1024)
    print(data.decode('utf-8'))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值