Python自学第十周(2)

协程
协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。
协程拥有本身的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其余地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。所以:
协程能保留上一次调用时的状态(即全部局部状态的一个特定组合),每次过程重入时,就至关于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。
协程的好处:
* 无需线程上下文切换的开销
* 无需原子操做锁定及同步的开销
    *   "原子操做(atomic operation)是不须要synchronized",所谓原子操做是指不会被线程调度机制打断的操做;这种操做一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另外一个线程)。原子操做能够是一个步骤,也能够是多个操做步骤,可是其顺序是不能够被打乱,或者切割掉只执行部分。视做总体是原子性的核心。
* 方便切换控制流,简化编程模型
* 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。因此很适合用于高并发处理。
缺点:
* 没法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程须要和进程配合才能运行在多CPU上.固然咱们平常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
* 进行阻塞(Blocking)操做(如IO时)会阻塞掉整个程序
 
 
 
一、yield
 1 #Author: Zachary
 2 import time
 3 import queue
 4  
 5 def consumer(name):
 6     print("--->starting eating baozi...")
 7     while True:
 8         new_baozi = yield
 9         print("[%s] is eating baozi %s" % (name, new_baozi))
10         # time.sleep(1)
11  
12 def producer():
13     r = con.__next__()
14     r = con2.__next__()
15     n = 0
16     while n < 5:
17         n += 1
18         print("\033[32;1m[producer]\033[0m is making baozi %s" % n)
19         con.send(n)
20         con2.send(n)
21  
22 if __name__ == '__main__':
23     con = consumer("c1")
24     con2 = consumer("c2")
25     p = producer()
 
 
 
二、greenlet(封装的协程)
 1 #Author: Zachary
 2 from greenlet import greenlet
 3 def test1():
 4     print(12)
 5     gr2.switch()
 6     print(34)
 7     gr2.switch()
 8  
 9 def test2():
10     print(56)
11     gr1.switch()
12     print(78)
13  
14 gr1 = greenlet(test1)    #启动一个协程
15 gr2 = greenlet(test2)
16 gr1.switch()
switch会跳转
 
 
 
三、Gevent
Gevent 是一个第三方库,能够轻松经过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet所有运行在主程序操做系统进程的内部,但它们被协做式地调度。
 
 1 #Author: Zachary
 2  
 3 import gevent
 4  
 5 def func1():
 6     print('\033[31;1m李闯在跟海涛搞...\033[0m')
 7     gevent.sleep(2)
 8     print('\033[31;1m李闯又回去跟继续跟海涛搞...\033[0m')
 9  
10 def func2():
11     print('\033[32;1m李闯切换到了跟海龙搞...\033[0m')
12     gevent.sleep(1)
13     print('\033[32;1m李闯搞完了海涛,回来继续跟海龙搞...\033[0m')
14 def func3():
15     print("running func3")
16     gevent.sleep(0)
17     print("running func3 again")
18  
19 gevent.joinall([
20     gevent.spawn(func1),
21     gevent.spawn(func2),
22     gevent.spawn(func3),
23 ])
 
 
 
 
 
 
协程之爬虫
 1 #Author: Zachary
 2 from urllib import request
 3 import time
 4 import gevent
 5 from gevent import monkey
 6 monkey.patch_all()
 7 def f(url):
 8     print("GET: %s" %url)
 9     resp = request.urlopen(url)
10     data = resp.read()
11     print("%d bytes received from %s"%(len(data),url))
12 urls = ['https://www.python.org/',
13         'https://www.yahoo.com/',
14         'https://github.com/'
15         ]
16 time_start = time.time()
17 for url in urls:
18     f(url)
19 print("同步cost",time.time() - time_start)
20 async_time_start = time.time()
21 gevent.joinall([
22         gevent.spawn(f, 'https://www.python.org/'),
23         gevent.spawn(f, 'https://www.yahoo.com/'),
24         gevent.spawn(f, 'https://github.com/'),
25 ])
26 print("异步cost",time.time() - async_time_start)
 
 
 
 
 
协程之socket
能够实现异步
 
服务端
 1 #Author: Zachary
 2 import sys
 3 import socket
 4 import time
 5 import gevent
 6 from gevent import socket, monkey
 7 monkey.patch_all()
 8  
 9 def server(port):
10     s = socket.socket()
11     s.bind(('0.0.0.0', port))
12     s.listen(500)
13     while True:
14         cli, addr = s.accept()
15         gevent.spawn(handle_request, cli)
16  
17 def handle_request(conn):
18     try:
19         while True:
20             data = conn.recv(1024)
21             print("recv:", data)
22             conn.send(data)
23             if not data:
24                 conn.shutdown(socket.SHUT_WR)
25     except Exception as  ex:
26         print(ex)
27     finally:
28         conn.close()
29  
30 if __name__ == '__main__':
31     server(8001)
 
 
客户端
 1 #Author: Zachary
 2 import socket
 3 HOST = 'localhost'  # The remote host
 4 PORT = 8001  # The same port as used by the server
 5 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 6 s.connect((HOST, PORT))
 7 while True:
 8     msg = bytes(input(">>:"), encoding="utf8")
 9     s.sendall(msg)
10     data = s.recv(1024)
11     # print(data)
12     print('Received', repr(data))
13 s.close()
 
 
 
 
IO多路复用
 
 
 
 
 
能够多看看上面这两个
只有在非阻塞模式下才能实现IO多路复用
select代码实现
 
 
服务端
 1 #Author: Zachary
 2 import select,socket,queue
 3  
 4 server = socket.socket()
 5 server.bind(('localhost',9000))
 6 server.listen(1024)
 7 server.setblocking(False)   #不阻塞
 8 inputs = [server,]
 9 outputs = []
10 while True:
11     readable,writeable,exceptional = select.select(inputs,outputs,inputs)
12     print(readable,writeable,exceptional)
13     for r in readable:
14         if r is server:   #表明来了一个新链接,创建链接
15             conn, addr = server.accept()
16             print("来了个新链接", addr)
17             inputs.append(conn)     #由于这个新创建的链接尚未发数据过来,如今就接收的话程序就报错了
18             #因此想要实现这个客户端发数据来时server端能知道,就须要让select再检测这个conn
19         else:
20             data = r.recv(1024)
21             print("收到数据>>>",data)
22             r.send(data)
23             print("send done")

 

 
 
客户端
 1 #Author: Zachary
 2 import socket
 3 HOST = 'localhost'  # The remote host
 4 PORT = 9000  # The same port as used by the server
 5 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 6 s.connect((HOST, PORT))
 7 while True:
 8     msg = bytes(input(">>:"), encoding="utf8")
 9     s.sendall(msg)
10     data = s.recv(1024)
11     # print(data)
12     print('Received', repr(data))
13 s.close()
 
能够开启多个客户端
 
 
 
服务端
 1 #Author: Zachary
 2 import select,socket,queue
 3  
 4 server = socket.socket()
 5 server.bind(('localhost',9000))
 6 server.listen(1024)
 7 server.setblocking(False)   #不阻塞
 8 msg_dic = {}
 9 inputs = [server,]
10 outputs = []
11 while True:
12     readable,writeable,exceptional = select.select(inputs,outputs,inputs)
13     print(readable,writeable,exceptional)
14     for r in readable:
15         if r is server:   #表明来了一个新链接,创建链接
16             conn, addr = server.accept()
17             print("来了个新链接", addr)
18             inputs.append(conn)     #由于这个新创建的链接尚未发数据过来,如今就接收的话程序就报错了
19             #因此想要实现这个客户端发数据来时server端能知道,就须要让select再检测这个conn
20             msg_dic[conn] = queue.Queue()    #初始化一个队列,后面存要返回给客户端的数据
21         else:
22             data = r.recv(1024)
23             print("收到数据>>>",data)
24             msg_dic[r].put(data)
25             outputs.append(r)    #放入返回的连接队列里
26             # r.send(data)
27             # print("send done")
28     for w in writeable:  #要返回给客户端的连接列表
29         data_to_client = msg_dic[w].get()
30         w.send(data_to_client)
31         outputs.remove(w)   #确保下次循环的时候writeable,不返回这个已经处理完的链接了
32     for e in exceptional:
33         if e in outputs:
34             outputs.remove(e)
35         inputs.remove(e)
36         del msg_dic[e]
客户端不变
 
 
 
selectors模块
 1 import selectors
 2 import socket
 3 sel = selectors.DefaultSelector()
 4 def accept(sock, mask):
 5     conn, addr = sock.accept()  # Should be ready
 6     print('accepted', conn, 'from', addr)
 7     conn.setblocking(False)
 8     sel.register(conn, selectors.EVENT_READ, read)
 9 def read(conn, mask):
10     data = conn.recv(1000)  # Should be ready
11     if data:
12         print('echoing', repr(data), 'to', conn)
13         conn.send(data)  # Hope it won't block
14     else:
15         print('closing', conn)
16         sel.unregister(conn)
17         conn.close()
18 sock = socket.socket()
19 sock.bind(('localhost', 10000))
20 sock.listen(100)
21 sock.setblocking(False)
22 sel.register(sock, selectors.EVENT_READ, accept)
23 while True:
24     events = sel.select()
25     for key, mask in events:
26         callback = key.data
27         callback(key.fileobj, mask)

 

 
 
客户端可使用
 1 #Author: Zachary
 2 import socket
 3 import sys
 4 messages = [ b'This is the message. ',
 5              b'It will be sent ',
 6              b'in parts.',
 7              ]
 8 server_address = ('localhost', 9999)
 9 # Create a TCP/IP socket
10 socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
11           socket.socket(socket.AF_INET, socket.SOCK_STREAM),
12           socket.socket(socket.AF_INET, socket.SOCK_STREAM),
13           socket.socket(socket.AF_INET, socket.SOCK_STREAM),
14           ]
15 # Connect the socket to the port where the server is listening
16 print('connecting to %s port %s' % server_address)
17 for s in socks:
18     s.connect(server_address)
19 for message in messages:
20     # Send messages on both sockets
21     for s in socks:
22         print('%s: sending "%s"' % (s.getsockname(), message) )
23         s.send(message)
24     # Read responses on both sockets
25     for s in socks:
26         data = s.recv(1024)
27         print( '%s: received "%s"' % (s.getsockname(), data) )
28         if not data:
29             print(sys.stderr, 'closing socket', s.getsockname() )
30  
相关文章
相关标签/搜索