Python全栈之路-Day42

1 selectors模块

1.1 select模型实现

server:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/10

import socket
import select

phone = socket.socket()
phone.bind(('127.0.0.1',8081))
phone.listen(5)

phone.setblocking(False)

input_list = [phone,]

while True:
    r,w,e = select.select(input_list,[],[]) # 监听所有套接字状态,如果有套接字发生变化,则向下执行代码

    for obj in r:
        if obj == phone:
            conn,addr = obj.accept()
            print(conn)
            input_list.append(conn)   # 添加套接字对象
        else:
            try:  
                recv_data = obj.recv(1024)
                print(recv_data)
                send_data = input('>>> ')
                obj.send(send_data.encode())
            except Exception as e:
                input_list.remove(obj)
            if not send_data:  # linux系统不会报错,会发空数据给server端
                input_list.remove(obj)

phone.close()

client:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/10

import socket
phone = socket.socket()
phone.connect(('127.0.0.1',8081))

while True:
    send_data = input('>>> ')
    phone.send(send_data.encode())
    recv_data = phone.recv(1024)
    print(recv_data)

select模型的缺点:

  • 每次调用select都要将所有的fd(文件描述符(套接字))拷贝到内核空间,导致效率降低
  • 监听模式为遍历所有的fd是否有数据访问(最重要的问题)
  • 最大连接数不能超过1024(可以修改,但不建议)

poll模型与select模型对比:

  • 最大连接数没有限制(不考虑系统的最大连接数和文件描述符限制)

epoll模型(通过三个函数实现)和select模型对比:

  • 第一个函数:创建一个epoll句柄,将所有的fd(文件描述符(套接字))拷贝到内核空间,但只需要拷贝一次
  • 第三个函数(回调函数:某一个函数(第二个函数)成功完成之后会触发的函数):为所有的fd绑定一个回调函数,一旦有数据访问,触发绑定的回调函数,回调函数将fd放到一个链表中

1.2 selectors模块实现

server:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/11

import selectors   # 基于select模块实现的IO多路复用,建议使用
import socket

sock = socket.socket()
sock.bind(('127.0.0.1',8080))
sock.listen(5)
sock.setblocking(False)

def read(conn,mask):
    recv_data = conn.recv(1024)
    print(recv_data.decode())
    send_data = input('>> ')
    conn.send(send_data.encode())


def accept(obj,mask):
    conn,addr = obj.accept()
    sel.register(conn,selectors.EVENT_READ,read)  # 注册客户端conn,并绑定read函数



sel = selectors.DefaultSelector()  # 根据具体平台选择最佳IO多路复用模型,比如在Linux,会自动选择epoll模型
sel.register(sock,selectors.EVENT_READ,accept)  # 注册sock对象,并绑定accept函数

while True:
    print('waiting...')
    events = sel.select()   # 监听
    for key,mask in events:
        key.data(key.fileobj,mask) # key.data (accept或read函数) key.fileobj (fd)

client:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/10

import socket
phone = socket.socket()
phone.connect(('127.0.0.1',8081))

while True:
    send_data = input('>>> ')
    phone.send(send_data.encode())
    recv_data = phone.recv(1024)
    print(recv_data)

2 队列(queue模块)

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/11

import queue

q = queue.Queue(5) # 默认是先进先出(FIFO)
q.put('msg01')
q.put('msg02')
q.put('msg02')
q.put('msg02')
q.put('msg02')
# q.put('msg02')  # 超过最大队列5时,会处于阻塞状态
# q.put('msg02',False) # 如果设置为非阻塞模式而且队列已经满了,则抛出队列溢出异常

print(q.get())
print(q.get())
print(q.get())

q.get(False) # 默认队列为空,默认是阻塞状态,如果改成False,如果队列为空,则抛出异常

# queue有点: 线程安全

# join和task_done
q = queue.Queue(5)
q.put('msg01')
q.put('msg02')

print(q.get())
q.task_done() # 表示某个任务完成。每一条get语句后需要一条task_done。
print(q.get())
q.task_done()

q.join()  # 阻塞进程,直到所有任务完成(队列为空),需要配合另一个方法task_done
print('ending')

# 先进后出模式LIFO
q = queue.LifoQueue()
q.put('msg01')
q.put('msg02')
q.put('msg03')
print(q.get())
print(q.get())
print(q.get())

# 优先级模式
q = queue.PriorityQueue()
q.put([3,'msg03'])
q.put([2,'msg02'])
q.put([1,'msg01'])
print(q.get())
print(q.get())
print(q.get())




'''
此包中的常用方法(q = Queue.Queue()):

q.qsize() 返回队列的大小
q.empty() 如果队列为空,返回True,反之False
q.full() 如果队列满了,返回True,反之False
q.full 与 maxsize 大小对应
q.get([block[, timeout]]) 获取队列,timeout等待时间
q.get_nowait() 相当q.get(False)非阻塞
q.put(item) 写入队列,timeout等待时间
q.put_nowait(item) 相当q.put(item, False)
q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() 实际上意味着等到队列为空,再执行别的操作

'''

3 生产者消费者模型

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

这就像,在餐厅,厨师做好菜,不需要直接和客户交流,而是交给前台,而客户去饭菜也不需要不找厨师,直接去前台领取即可,这也是一个解耦的过程。

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/11

import time,random
import queue,threading

q = queue.Queue()

def Producer(name):
  count = 0
  while count <10:
    print("making........")
    time.sleep(random.randrange(3))
    q.put(count)
    print('Producer %s has produced %s baozi..' %(name, count))
    count +=1
    #q.task_done()
    #q.join()
    print("ok......")
def Consumer(name):
  count = 0
  while count <10:
    time.sleep(random.randrange(4))
    if not q.empty():
        data = q.get()
        #q.task_done()
        #q.join()
        print(data)
        print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
    else:
        print("-----no baozi anymore----")
    count +=1

p1 = threading.Thread(target=Producer, args=('A',))
c1 = threading.Thread(target=Consumer, args=('B',))
# c2 = threading.Thread(target=Consumer, args=('C',))
# c3 = threading.Thread(target=Consumer, args=('D',))
p1.start()
c1.start()
# c2.start()
# c3.start()

posted on 2017-05-11 21:29  万越天  阅读(192)  评论(0编辑  收藏  举报