day10-python- coroutine \ asynchronous IO \ Database \ Cache

First, coroutine

Coroutine, also called micro-threads, shred. English Coroutine. What is the thread sentence description: coroutine is a user-lightweight thread state .

Coroutine has its own stack and register context. When coroutine scheduled handover, the context save registers and stack to another location, when cut back, context restore a previously saved registers and stack. therefore:

Coroutine retain the state when a call (i.e., a particular combination of all of the local state), during each reentrant, the equivalent of entering a call state, another way: once logic which exits into the the position of the stream.

Coroutine benefits:

  • No need to thread context switching overhead
  • You do not need atomic operations lock and synchronization overhead
    •   "Atomic operations (atomic operation) is not required the synchronized", refers to the so-called atomic operations will not be interrupted thread scheduling mechanism operation; this operation once started, has been run to the end, without any intermediate context switch (switch to another thread). Atomic operations may be one step, or may be a plurality of steps, but the order is not disrupted, or cut off only partially executed. Viewed as a whole is atomic core.
  • Facilitate the switch control flow, simplified programming model
  • High concurrency + scalability + low cost: a CPU million for the support coroutine is not a problem. So it is suitable for high concurrent processing.

Disadvantages:

  • Can not take advantage of multi-core resource: the nature of the coroutine is a single-threaded, it can not spend a single multiple-core CPU, the coroutine needs and processes with the vast majority of applications to run on multiple CPU Of course, we are prepared daily. this is not necessary, unless it is cpu-intensive applications.
  • Operations (such as when IO) were blocked (Blocking) will block out the entire program

Yield achieved using the example of the operation coroutine

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import time
import queue

def consumer(name):
    print("--->starting eating baozi...")
    while True:
        new_baozi = yield
        print("[%s] is eating baozi %s" % (name, new_baozi))
        # time.sleep(1)

def producer():
    r = con.__next__()
    r = con2.__next__()
    n = 0
    while n < 5:
        n += 1
        con.send(n)
        con2.send(n)
        print("\033[32;1m[producer]\033[0m is making baozi %s" % n)

if __name__ == '__main__':
    con = consumer("c1")
    con2 = consumer("c2")
    p = producer()

Examples see upstairs, I asked you if that is not done coroutine it? You say, I know what his mother ah, you said in front of a bunch of nonsense, but did not tell me the standard form of coroutine Yeah, I thought Ding eyes, think you said also, well, we give coroutine a standard definition that is in line with what conditions will be able to call coroutine:

  1. It must be implemented concurrently in only a single thread in
  2. Modify shared data without locking
  3. Save your user program control flow stack multiple contexts
  4. A coroutine encountered IO operation is automatically switched to another coroutine

Based on the above definition of the four points, we just used to achieve process yield and can not be considered eligible thread, because it has little function did not realize, point it?

 

Greenlet

greenlet implemented in C is a coroutine module, compared with the yield comes python, which allows you to freely switch between an arbitrary function, this function without the need for the generator to declare

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from greenlet import greenlet

def test1():
    print(12)
    gr2.switch()
    print(34)
    gr2.switch()

def test2():
    print(56)
    gr1.switch()
    print(78)

GR1 = greenlet (test1)    # start a coroutine 
GR2 = greenlet (test2)
gr1.switch()

I feel really use a generator than it has simple, but it seems to have not solved a problem encountered is IO operation, automatic switch, right?

 

peddled

Gevent is a third-party library, you can easily implement synchronous or asynchronous concurrent programming by gevent, the main mode is used in gevent Greenlet , it is a form of access Python C extension module lightweight coroutines. Greenlet all run inside the main operating system processes, but they are collaboratively scheduling.

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import peddled


def foo():
    print('Running in foo')
    gevent.sleep(2)
    print('Explicit context switch to foo again')


DEF bar ():
     Print ( ' Explicit bar precise context content to ' )
    gevent.sleep(1)
    print('Implicit context switch back to bar')

def func3():
    print("running func3")
    gevent.sleep (0)
    print("running func3 again")

gevent.joinall ([
    gevent.spawn (foo)   # 生成
    gevent.spawn (bar)
    gevent.spawn (FUNC3)
])

 

The performance difference between synchronous and asynchronous

import peddled
 
def task(pid):
    """
    Some non-deterministic task
    """
    gevent.sleep(0.5)
    print('Task %s done' % pid)
 
def synchronous():
    for i in range(1,10):
        task(i)
 
def asynchronous():
    threads = [gevent.spawn(task, i) for i in range(10)]
    gevent.joinall(threads)
 
print('Synchronous:')
synchronous()
 
print('Asynchronous:')
asynchronous()

The above procedure is an important part of the function to the task package Greenlet internal thread gevent.spawn. Greenlet initialization list stored in an array threads, the array is passed to this gevent.joinall function, which blocks the current process, and performs all the given greenlet. All greenlet execution flow only after the implementation will continue to go down.

 

It will automatically switch tasks encountered blocking IO

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from urllib import request
import gevent,time
from gevent import monkey

monkey.patch_all ()   # the current operating procedures of all io me alone cook mark

def f(url):
    print('GET: %s' % url)
    resp = request.urlopen(url)
    data = resp.read()
    print('%d bytes received from %s.' % (len(data), url))

urls = ['https://www.python.org/',
        'https://www.yahoo.com/',
        'https://github.com/']
time_start = time.time()
for url in urls:
    f(url)
print("同步cost",time.time() - time_start)

async_time_start = time.time()
gevent.joinall ([
    gevent.spawn (f, ' https://www.python.org/ ' ),
    gevent.spawn (f, ' https://www.yahoo.com/ ' ),
    gevent.spawn (f, ' https://github.com/ ' ),
])
print("异步cost",time.time() - async_time_start)

Under the concurrent multi-socket single-threaded through gevent

server side 

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys
import socket
import time
import gevent

from gevent import socket, monkey

monkey.patch_all()


def server(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        gevent.spawn(handle_request, cli)


def handle_request(conn):
    try:
        while True:
            data = conn.recv(1024)
            print("recv:", data)
            conn.send(data)
            if not data:
                conn.shutdown(socket.SHUT_WR)

    except Exception as  ex:
        print(ex)
    finally:
        conn.close()


if __name__ == '__main__':
    server(8001)

client side 

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket

HOST = 'localhost'  # The remote host
PORT = 8001  # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    msg = bytes(input(">>:"), encoding="utf8")
    s.sendall(msg)
    data = s.recv(1024)
    # print(data)
    #
    print('Received', repr(data))
s.close()

Concurrent sock connection 100

import socket
import threading

def sock_conn():

    client = socket.socket()

    client.connect(("localhost",8001))
    count = 0
    while True:
        #msg = input(">>:").strip()
        #if len(msg) == 0:continue
        client.send( ("hello %s" %count).encode("utf-8"))

        data = client.recv(1024)

        print("[%s]recv from server:" % threading.get_ident(),data.decode()) #结果
        count +=1
    client.close()


for i in range(100):
    t = threading.Thread(target=sock_conn)
    t.start()

Second, event-driven and asynchronous IO

Typically, when we write server processing model program, are the following models:
(1) each receive a request to create a new process to handle the request;
(2) each receive a request to create a new thread to handle the request;
(3) the receipt of each request, into a list of events to get the main process to handle requests by non-blocking I / O mode
Above in several ways, each has its advantages,
(1) method, create a new process because the overhead is relatively large, it will cause the server performance is relatively poor, but relatively simple to achieve.
(2) ways, due to the involved thread synchronization, there may be faced with deadlock and other issues.
(3) manner, when writing application code, logic more complex than the previous two.
Considering all factors, generally agreed that the first (3) ways most network servers methods used
 

Picture Talk talk event-driven model

In the UI programming, often corresponding to mouse clicks, mouse clicks first of all how to get it?
Method 1: Create a thread that has been circulating detect whether a mouse click, then this approach has the following disadvantages :
1. the CPU resources wasted, mouse click frequency may be very small, but still would have been scanning the thread loop detection, which CPU will cause a lot of waste of resources; if the scan-click interface is blocking it?
2. If it is blocked, the following will be such a problem, if we want to scan only a mouse click, but also scan the keyboard is depressed due to being blocked when scanning a mouse, you may never go scan the keyboard;
3. If you need to scan a cycle of equipment is very large, which in turn will lead to the problem of response time;
therefore, this method is very bad.

Second way: event-driven model is
present, most of the UI program are event-driven model, as a lot of UI platform will provide onClick () event, which represents a mouse down event. Event-driven model of the general idea is as follows:
1. there is an event (message) queue;
2. Press the mouse, this queue to increase a click event (message);
3. There cycles continuously removed from the event queue, depending on the events, call different functions, such as onClick (), onKeyDown () and the like;
4. events (messages) are generally each storing respective processing function pointers, so that each has its own message handler;

 

 

 

Event-driven programming is a programming paradigm, where the program execution flow is determined by external events. It features include an event loop, when an external event occurs using a callback mechanism to trigger the appropriate treatment. Two other common programming paradigm (single-threaded) synchronization and multi-threaded programming.

Let's compare and contrast it with the example of a single-threaded, multi-threaded and event-driven programming model. The following figure shows the work done over time these three modes program. This program has three tasks to complete each task is waiting for I / blocking their operation O. Time block on I / O operation has been marked spent in the gray boxes.

 

在单线程同步模型中,任务按照顺序执行。如果某个任务因为I/O而阻塞,其他所有的任务都必须等待,直到它完成之后它们才能依次执行。这种明确的执行顺序和串行化处理的行为是很容易推断得出的。如果任务之间并没有互相依赖的关系,但仍然需要互相等待的话这就使得程序不必要的降低了运行速度。

在多线程版本中,这3个任务分别在独立的线程中执行。这些线程由操作系统来管理,在多处理器系统上可以并行处理,或者在单处理器系统上交错执行。这使得当某个线程阻塞在某个资源的同时其他线程得以继续执行。与完成类似功能的同步程序相比,这种方式更有效率,但程序员必须写代码来保护共享资源,防止其被多个线程同时访问。多线程程序更加难以推断,因为这类程序不得不通过线程同步机制如锁、可重入函数、线程局部存储或者其他机制来处理线程安全问题,如果实现不当就会导致出现微妙且令人痛不欲生的bug。

在事件驱动版本的程序中,3个任务交错执行,但仍然在一个单独的线程控制中。当处理I/O或者其他昂贵的操作时,注册一个回调到事件循环中,然后当I/O操作完成时继续执行。回调描述了该如何处理某个事件。事件循环轮询所有的事件,当事件到来时将它们分配给等待处理事件的回调函数。这种方式让程序尽可能的得以执行而不需要用到额外的线程。事件驱动型程序比多线程程序更容易推断出行为,因为程序员不需要关心线程安全问题。

当我们面对如下的环境时,事件驱动模型通常是一个好的选择:

  1. 程序中有许多任务,而且…
  2. 任务之间高度独立(因此它们不需要互相通信,或者等待彼此)而且…
  3. 在等待事件到来时,某些任务会阻塞。

当应用程序需要在任务间共享可变的数据时,这也是一个不错的选择,因为这里不需要采用同步处理。

网络应用程序通常都有上述这些特点,这使得它们能够很好的契合事件驱动编程模型。

此处要提出一个问题,就是,上面的事件驱动模型中,只要一遇到IO就注册一个事件,然后主程序就可以继续干其它的事情了,只到io处理完毕后,继续恢复之前中断的任务,这本质上是怎么实现的呢?哈哈,下面我们就来一起揭开这神秘的面纱。。。。

三、select、poll、epoll三者的区别

 https://www.cnblogs.com/alex3714/p/4372426.html

四、IO多路复用

https://www.cnblogs.com/alex3714/articles/5876749.html

五、select/selectors模块

select 多并发socket 例子

select socket server

import select
import socket
import sys
import queue


server = socket.socket()
server.setblocking(0)

server_addr = ('localhost',10000)

print('starting up on %s port %s' % server_addr)
server.bind(server_addr)

server.listen(5)


inputs = [server, ] #自己也要监测呀,因为server本身也是个fd
outputs = []

message_queues = {}

while True:
    print("waiting for next event...")

    readable, writeable, exeptional = select.select(inputs,outputs,inputs) #如果没有任何fd就绪,那程序就会一直阻塞在这里

    for s in readable: #每个s就是一个socket

        if s is server: #别忘记,上面我们server自己也当做一个fd放在了inputs列表里,传给了select,如果这个s是server,代表server这个fd就绪了,
            #就是有活动了, 什么情况下它才有活动? 当然 是有新连接进来的时候 呀
            #新连接进来了,接受这个连接
            conn, client_addr = s.accept()
            print("new connection from",client_addr)
            conn.setblocking(0)
            inputs.append(conn) #为了不阻塞整个程序,我们不会立刻在这里开始接收客户端发来的数据, 把它放到inputs里, 下一次loop时,这个新连接
            #就会被交给select去监听,如果这个连接的客户端发来了数据 ,那这个连接的fd在server端就会变成就续的,select就会把这个连接返回,返回到
            #readable 列表里,然后你就可以loop readable列表,取出这个连接,开始接收数据了, 下面就是这么干 的

            message_queues[conn] = queue.Queue() #接收到客户端的数据后,不立刻返回 ,暂存在队列里,以后发送

        else: #s不是server的话,那就只能是一个 与客户端建立的连接的fd了
            #客户端的数据过来了,在这接收
            data = s.recv(1024)
            if data:
                print("收到来自[%s]的数据:" % s.getpeername()[0], data)
                message_queues[s].put(data) #收到的数据先放到queue里,一会返回给客户端
                if s not  in outputs:
                    outputs.append(s) #为了不影响处理与其它客户端的连接 , 这里不立刻返回数据给客户端


            else:#如果收不到data代表什么呢? 代表客户端断开了呀
                print("客户端断开了",s)

                if s in outputs:
                    outputs.remove(s) #清理已断开的连接

                inputs.remove(s) #清理已断开的连接

                del message_queues[s] ##清理已断开的连接


    for s in writeable:
        try :
            next_msg = message_queues[s].get_nowait()

        except queue.Empty:
            print("client [%s]" %s.getpeername()[0], "queue is empty..")
            outputs.remove(s)

        else:
            print("sending msg to [%s]"%s.getpeername()[0], next_msg)
            s.send(next_msg.upper())


    for s in exeptional:
        print("handling exception for ",s.getpeername())
        inputs.remove(s)
        if s in outputs:
            outputs.remove(s)
        s.close()

        del message_queues[s]

select socket client

import socket
import sys

messages = [ b'This is the message. ',
             b'It will be sent ',
             b'in parts.',
             ]
server_address = ('localhost', 10000)

# Create a TCP/IP socket
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
          socket.socket(socket.AF_INET, socket.SOCK_STREAM),
          ]

# Connect the socket to the port where the server is listening
print('connecting to %s port %s' % server_address)
for s in socks:
    s.connect(server_address)

for message in messages:

    # Send messages on both sockets
    for s in socks:
        print('%s: sending "%s"' % (s.getsockname(), message) )
        s.send(message)

    # Read responses on both sockets
    for s in socks:
        data = s.recv(1024)
        print( '%s: received "%s"' % (s.getsockname(), data) )
        if not data:
            print(sys.stderr, 'closing socket', s.getsockname() )

 

selectors模块

这个模块允许高级和高效的I/O多路复用,建立在选择模块原语的基础上。我们鼓励用户使用这个模块,除非他们希望对所使用的os级原语进行精确控制。

import selectors
import socket
 
sel = selectors.DefaultSelector()
 
def accept(sock, mask):
    conn, addr = sock.accept()  # Should be ready
    print('accepted', conn, 'from', addr)
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)
 
def read(conn, mask):
    data = conn.recv(1000)  # Should be ready
    if data:
        print('echoing', repr(data), 'to', conn)
        conn.send(data)  # Hope it won't block
    else:
        print('closing', conn)
        sel.unregister(conn)
        conn.close()
 
sock = socket.socket()
sock.bind(('localhost', 10000))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
 
while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data
        callback(key.fileobj, mask)

作业一:

题目:IO多路复用版FTP

需求:

  1. 实现文件上传及下载功能
  2. 支持多连接并发传文件
  3. 使用select or selectors

Guess you like

Origin www.cnblogs.com/guantou1992/p/11777444.html