协程

迭代器内置iter方法---为了实现for循环,for循环的本质是调用可迭代对象的iter方法
迭代器也是可迭代对象


并发的本质--只是看起来是同时运行,其本质是 切+保存状态

协程--单线程实现并发。又称微线程,纤程。Coroutine。
用户从应用程序级别控制单线程下任务的切换,**一定是IO切**

一句话说明什么是线程---用户态的轻量级线程,即协程是由用户程序自己控制调度的
用户态(程序状态器指令字-0),内核态(程序状态器指令字-1)---自己去了解

优点--最大限度利用CPU
缺点--只能在单线程下,无法利用多核

特点:
必须在只有一个单线程里实现并发,
修改共享数据不需要加锁--没有同时运行的
用户程序里自己保存多个控制流的上下文栈
gevent模块--自动切换,检测IO

greenlet和yield一样都无法检测IO,然后自动切换,好处greenlet比yield切换方式更加方便
import time
def producer():
    res=[]
    for i in range(10000000):
        res.append(i)
    return res

def consumer(res):
    pass

start=time.time()
res=producer()
consumer(res)
stop=time.time()
print(stop-start)



import time
def producer():
    g=consumer()
    next(g)
    for i in range(10000000):
        g.send(i)

def consumer():
    while True:
        n=yield

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



import time
def producer():
    g=consumer()
    next(g)
    for i in range(10000000):
        print('producer')
        time.sleep(10)
        g.send(i)


def consumer():
    while True:
        print('consumer')
        n=yield


producer()
yield方法

 

from greenlet import greenlet
import time
def eat(name):
    print("%s eat 1"%name)
    time.sleep(10)
    g2.switch("alex")
    print("%s eat 2"%name)
    g2.switch()

def play(name):
    print("%s play 1"%name)
    g1.switch()
    print("%s play 2"%name)

g1 = greenlet(eat)
g2 = greenlet(play)
greenlet模块

 

gevent模块
方法
g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的
g2=gevent.spawn(func2)

g1.join() #等待g1结束
g2.join() #等待g2结束
gevent.sleep()#--模拟IO

或者上述两步合作一步:gevent.joinall([g1,g2])

g1.value#拿到func1的返回值

 

import gevent,time
def eat(name):
    print("%s eat 1"%name)
    gevent.sleep(3)
    print("%s eat 2"%name)
def play(name):
    print("%s play 1"%name)
    gevent.sleep(1)
    print("%s play 2"%name)
start = time.time()
g1 = gevent.spawn(eat,"alex")
g2 = gevent.spawn(play,"alex")
# g1.join()
# g2.join()
gevent.joinall([g1,g2])   #--等价上面两行
stop = time.time()
print(stop-start)
gevent模块
import gevent,time,os
def eat():
    print("%s eat 1"%os.getpid())
    gevent.sleep(3)
    print("%s eat 2"%os.getpid())
def play():
    print("%s play 1"%os.getpid())
    gevent.sleep(1)
    print("%s play 2"%os.getpid())

start = time.time()
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
# g1.join()
# g2.join()
gevent.joinall([g1,g2])   #--等价上面两行
stop = time.time()
print(stop-start)
只开一个进程
import gevent,time
from threading import current_thread
def eat():
    print("%s eat 1"%current_thread().getName())
    gevent.sleep(3)
    print("%s eat 2"%current_thread().getName())
def play():
    print("%s play 1"%current_thread().getName())
    gevent.sleep(1)
    print("%s play 2"%current_thread().getName())

start = time.time()
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
# g1.join()
# g2.join()
gevent.joinall([g1,g2])   #--等价上面两行
stop = time.time()
print(stop-start)
只开一个线程
from gevent import monkey;monkey.patch_all()
import gevent,time
from threading import current_thread
def eat():
    print("%s eat 1"%current_thread().getName())
    time.sleep(3)
    print("%s eat 2"%current_thread().getName())
def play():
    print("%s play 1"%current_thread().getName())
    time.sleep(1)
    print("%s play 2"%current_thread().getName())

start = time.time()
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
gevent.joinall([g1,g2])   #--等价上面两行
stop = time.time()
print(stop-start)
加补丁识别所有阻塞
from gevent import monkey;monkey.patch_all()
from threading import current_thread
import gevent,requests

def get(url):
    print("%s is getting %s"%(current_thread().getName(),url))
    response = requests.get(url)
    if response.status_code == 200:
        print({"url":len(response.text)})

g1 = gevent.spawn(get,"http://www.baidu.com")
g2 = gevent.spawn(get,"http://www.python.org")
g3 = gevent.spawn(get,"http://www.JD.com")

g1.join()
g2.join()
g3.join()
单线程并发爬页面

 

单线程实现并发套接字
from gevent import monkey;monkey.patch_all()    # 必须必须写到脚本最开始
from socket import *
import gevent

def myserver(ip,port):
    s = socket(AF_INET,SOCK_STREAM)
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind((ip,port))
    s.listen(5)
    while True:
        conn,addr = s.accept()
        print("%s:%s"%(addr[0],addr[1]))
        g1 = gevent.spawn(talk, conn, addr)

def talk(conn,addr):
    while True:
        try:
            data = conn.recv(1024)
            print("%s : %s [%s]"%(addr[0],addr[1],data))
            if not data:break
            conn.send(data.upper())
        except ConnectionResetError:
            break
    conn.close()

if __name__ == '__main__':
    myserver("127.0.0.1",8077)
server端
# from socket import *
#
# client = socket(AF_INET,SOCK_STREAM)
# client.connect(("127.0.0.1",8077))
#
# while True:
#     msg = input(">>>")
#     if not msg:continue
#     client.send(msg.encode("utf-8"))
#     data = client.recv(1024)
#     print(data.decode("utf-8"))
#

from threading import Thread
from socket import *

def myclient():
    c = socket(AF_INET, SOCK_STREAM)
    c.connect(("127.0.0.1", 8077))

    while True:
        c.send("hello".encode("utf-8"))
        print("send")
        data = c.recv(1024)
        print(data.decode("utf-8"))

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

 

******并发的方式 --- 多进程,多线程,单线程开协程

 

posted @ 2017-10-17 16:27  chitalu  阅读(102)  评论(0)    收藏  举报