The IO model of Python Learning _day39_ concurrent programming

Source: Internet
Author: User

For a network IO (here we read for example), it involves two system objects, one that calls the IO process (or thread), and the other is the system kernel (kernel). When a read operation occurs, the operation goes through two stages: waiting for data preparation (waiting for the database is ready), and copying the data from the kernel to the process (the Copying the data from the kernel to the pro Cess). Common main IO models are described below:

One, blocking io (blocking IO)

In Linux, all sockets are blocking by default, and a typical read operation flow is probably this:

When the user process invokes the RECVFROM system call, Kernel begins the first phase of IO: Preparing the data. For network IO, there are times when the data has not arrived at the beginning (for example, a full UDP packet has not been received), and kernel waits for enough data to arrive.

On this side of the user process, the entire process is blocked. When kernel waits until the data is ready, it copies the data from the kernel to the user's memory, and then kernel returns the result, and the user process removes the block state and re-runs it.

Virtually all IO interfaces (including the socket interface) are blocking, unless specifically specified. This poses a big problem for network programming, such as when calling recv (1024), the thread will be blocked, during which time the thread will be unable to perform any operations or respond to any network requests. To resolve this approach we have previously used open multi-process, multi-threaded, process pool, thread pool. However, in the way of multi-process or all-threading, when encountering a connection request that responds to hundreds or thousands of simultaneous requests, no matter how many threads or processes will take up the system resources, reduce the efficiency of the system to the outside, and the thread and process itself will be more easily into the suspended animation state. The "Thread pool" and "connection pooling" techniques are only to some extent mitigated by the frequent invocation of the IO interface for resource consumption. Moreover, the so-called "pool" always has its upper limit, when the request greatly exceeds the upper limit, the "pool" composed of the system response to the outside world is not much better than when there is no pool. So using the pool must consider the scale of the response it faces and adjust the size of the pool based on the response scale.

  The "thread pool" or "Connection pool" may alleviate some of the stress, but not all of them, in response to the thousands or even thousands of client requests that may appear in the previous example. In short, multithreaded models can easily and efficiently solve small-scale service requests, but in the face of large-scale service requests, multithreading model will encounter bottlenecks, you can use non-blocking interface to try to solve the problem.

second, non-blocking IO (non-blocking io)

 Under Linux, you can make it non-blocking by setting the socket. When you perform a read operation on a non-blocking socket, the process looks like this:

As you can see, when the user process issues a read operation, if the data in kernel is not ready, it does not block the user process, but returns an error immediately. From the user process point of view, it initiates a read operation and does not need to wait, but immediately gets a result. When the user process determines that the result is an error, it knows that the data is not ready, so the user can do something else in the interval between this time and the next time the read query is initiated, or send the read operation directly again. Once the data in the kernel is ready and again receives the system call of the user process, it immediately copies the data to the user's memory (this phase is still blocked) and returns. therefore, in non-blocking IO, the user process is actually required to constantly proactively ask kernel data ready to be prepared.

Instance:

#Service side: fromSocketImport*s=socket () s.setsockopt (SOL_SOCKET,SO_REUSEADDR,1) S.bind ('127.0.0.1', 8090) ) s.setblocking (False)#set to non-blocking modeS.listen (5) conn_l=[] whileTrue:Try: Conn,addr=s.accept ()#Gets the connection, throws an exception if it encounters a block blockingioerror        Print('Get Connected%s:%s'% (addr[0],addr[1])) conn_l.append (conn)#Add a successful conn to the list    exceptBlockingioerror:#After you throw this exception, execute the following codeDel_l=[]        Print('There are no connections available.')         forConninchconn_l:#loops the successful conn list for communication            Try: Data=CONN.RECV (1024)                if  notdata:del_l.append (conn)ContinueConn.send (Data.upper ())exceptBlockingioerror:#If you encounter a traffic jam, skip                Pass            exceptConnectionerror:#If a connection exception is encountered, add a secondary connection to a listdel_l.append (conn) forConninchdel_l:#loop to remove disconnected connections and avoid throwing exceptions again next timeConn_l.remove (conn) conn.close ()#Client: fromSocketImport*C=socket (Af_inet,sock_stream) c.connect (('127.0.0.1', 8090)) whiletrue:msg=input ('>>:')    if  notMsgContinuec.send (Msg.encode ('Utf-8')) Data=C.RECV (1024)    Print(Data.decode ('Utf-8'))

Although the above non-blocking IO has the advantage of reducing blocking, it is difficult to hide its main two major drawbacks: the cyclic call query will greatly increase the CPU's share; the response delay of the task completion is increased because the data is ready to be completed, but the data is received only when the next query is made to this connection. therefore, non-blocking IO is never recommended.

Tri, multiplexed io (io multiplexing)

The basic principle of the select/epoll is that the function will constantly poll all sockets that are responsible, and when a socket has data arrives, notifies the user of the process. It's process

When the user process invokes select, the entire process is blocked, and at the same time, kernel "monitors" all select-responsible sockets, and when the data in any one socket is ready, select returns. This time the user process then invokes the read operation, copying the data from the kernel to the user process. This figure is not much different from the blocking IO diagram, in fact it's even worse. Because there are two system calls (select and recvfrom) that need to be used, blocking IO only calls a system call (Recvfrom). However, the advantage of using select is that it can handle multiple connection at the same time.
In a multiplexed model, for each socket, it is generally set to non-blocking, but, as shown, the entire user's process is always block. Only the process is the block of the Select function, not the socket IO.

Instance:

#Service Side fromSocketImport*ImportSelects=socket (af_inet,sock_stream) s.setsockopt (sol_socket,so_reuseaddr,1) S.bind ('127.0.0.1', 8081)) S.listen (5) s.setblocking (False)#the interface for setting the socket is non-blockingRead_l=[S,] whiletrue:r_l,w_l,x_l=Select.select (read_l,[],[])Print(r_l) forReady_objinchr_l:ifReady_obj = =s:conn,addr=ready_obj.accept ()#at this point the ready_obj equals sread_l.append (conn)Else:            Try: Data=READY_OBJ.RECV (1024)#at this point the ready_obj equals conn                if  notdata:ready_obj.close () read_l.remove (ready_obj)ContinueReady_obj.send (Data.upper ())exceptConnectionResetError:ready_obj.close () read_l.remove (ready_obj)#Client fromSocketImport*C=socket (Af_inet,sock_stream) c.connect (('127.0.0.1', 8081)) whiletrue:msg=input ('>>:')    if  notMsgContinuec.send (Msg.encode ('Utf-8')) Data=C.RECV (1024)    Print(Data.decode ('Utf-8'))

Iv. Asynchronous IO (asynchronous I/O)

As the name implies, Async is not waiting for the result in place, the user process initiates the read operation, immediately can start to do other things. On the other hand, from the perspective of kernel, when it receives a asynchronous read, first it returns immediately, so no block is generated for the user process. Then, kernel waits for the data to be ready and then copies the data to the user's memory, and when all this is done, kernel sends a signal to the user process to tell it that the read operation is complete.

As described above, the difference between non-blocking io and asynchronous io is obvious. In non-blocking io, although the process will not be blocked for most of the time, it still requires the process to go to the active check, and when the data is ready, it is also necessary for the process to proactively call Recvfrom to copy the data to the user's memory. and asynchronous Io is completely different. It's like a user process handing over an entire IO operation to someone else (kernel) and then sending a signal notification when someone finishes it. During this time, the user process does not need to check the status of the IO operation, nor does it need to actively copy the data.

The IO model of Python Learning _day39_ concurrent programming

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.