Concurrent Concurrent Programming Python basis №⑧ Concluded: IO model

1, details

  

To better understand the IO model, we need to review in advance at: synchronous, asynchronous, blocking, non-blocking 

synchronous (synchronous): When a task is completed need to rely on another task, only waiting to be dependent on the completion of the task, dependent tasks can be considered complete,
it is a reliable task sequence. Either succeed are successful, failed and failed, two state of the task can be consistent.

Asynchronous (asynchronous): is no need to wait to be dependent on completion of the task, but notification is dependent on what work tasks to complete, depending on the tasks executed immediately,
as long as they completed the task is complete. As a final task was dependent on whether truly complete, depending on its mission can not be determined, so it is unreliable task sequence.

Blocking and non-blocking these two concepts and procedures (thread) to wait for message notification (does not matter synchronous or asynchronous)
state concerned. That is the main blocking and non-blocking state when the angle of the program (thread) to wait for the message notification.

In short: 
sync after submitting a job to wait for the task is finished
asynchronous simply submit the task, do not wait for the task is finished you can do other things
blocking recv recvfrom accept
non-blocking

network IO under Linux environment Richard Stevens proposed five two models:
. 1: blocking IO blocking IO
2: a nonblocking IO nonblocking IO
. 3: IO multiplexing IO multiplexer
4. signal driven IO signal for driving IO
. 5: asynchronous IO asynchronous IO

objects and repeat steps involved in at IO occurs. For a networkO (here we read, for example), it would involve two system objects,
one is calling this the IO process (or thread),
the other is the system kernel (kernel). When a read operation occurs, the operator will experience two phases:

1) Wait for data preparation (The Waiting for Data to BE READY)
2) to copy the data from the kernel process (Copying the data from the kernel to the process)


Next, exemplifiedThe first three commonly used models .
Blocking IO, often encountered. In linux, by default, all socket is blocking, a typical read operation process something like this:
1) a user process, the system calls the recvfrom method, kernel began the first phase of the IO: Preparing data;
for network io, many times the data has not yet arrived at the beginning (for example, has not yet received a complete UDP packet), this time the kernel will wait for enough data to come.
2) system process: First prepare the data, and then copy the data, and finally return the result to the user process.
In the process the user side, the whole process will be blocked. When the kernel wait until the data is ready, it will copy data from kernel to user memory, and then returns the result kernel,
the user process before lifting the state of the block, up and running again.

Therefore, blocking IO feature is executed in two stages IO (waiting for data and copies of data in two stages) are a block.

In fact, unless otherwise specified, almost all IO interfaces (including socket interfaces) are blocking the. This gives network programming has brought a lot of problems,
such as at the same time calling recv (1024), the thread will be blocked during this time, the thread will not be able to perform any operation or respond to any network requests.

A simple solution :
  use multiple threads (or processes) on the server side. The purpose multi-thread (or process) is to allow each connection has a separate thread (or process),
so any blocking a connection will not affect other connections.

Question of the program are:
  to open multiple processes or threads all the way in the face while responding to a connection request to hundreds of the road, regardless of multi-threaded or multi-process would seriously occupy system resources,
Reduce system efficiency response to the outside world, but also threads and processes themselves easier access to state of suspended animation.

Improvements :
Many programmers might consider using the "thread pool" or "connection pooling." "Thread pool" to reduce the frequency of thread creation and destruction, which maintains a reasonable number of threads,
and let idle thread again take on new tasks. "Connection Pool" to maintain the buffer pool connection, try to reuse existing connections, reducing the frequency of creating and close connections.
Both techniques can be very good to reduce system overhead, it has been widely used in many large systems, such as websphere, tomcat and a variety of databases.

In fact, the improved scheme is also problematic:

"thread pool" and "connection pooling" technology only to ease the frequent calls to bring IO interface resource consumption to some extent. Moreover, the so-called "pool" always has its upper limit,
when requested much higher than the upper limit, "pool" system consisting of a response to the outside world no better than when the pool is not much better effect. So using the "pool" must be considered in response to the size of its face,
and the size of the "pool" in response to the scale of adjustment.

Corresponds to the example of the face may also occur thousands or even thousands of times the client requests, "thread pool" or "connection pool" may be able to relieve some of the pressure, but
can not solve all the problems. In short, multi-threading model can be convenient and efficient solution to small-scale service requests, service requests but in the face of large-scale, multi-threading model will also encounter a problem,
you can use non-blocking interfaces to try to solve this problem.


2_ nonblocking IO (noneblocking IO)

  under linux, may be provided by a socket so that it becomes non-blocking. When performing a read operation on a non-blocking, the process is like this:

 

 

    As it can be seen from the figure, when a user process issues a read operation, if the data kernel is not ready, then it does not block the user process, but immediately return a 
one error. From the perspective of the user process, it initiates a read operation after, does not need to wait, but immediately got a result. User process is the result of a judgment error,
it knows that the data was not ready, so the user can then initiate inquiries do other things within the read time interval, or sent directly read this operation again in the next.

Once the data is ready for kernel, and again received a system call user process, then it will immediately copy the data to the user memory (this stage is still
blocked), and then return. That recvform non-blocking system call after call, and the process is not blocked, the kernel immediately returned to the process, if the data is not ready,
at this time will return an error. After the return process, you can do something else, and then initiate recvform system call. Repeat the above process, the cycle will be recvfrom
system call. This process is commonly referred to as polling. Check the kernel polling data until the data is ready, and then copy the data to process, for data processing. Note that a copy of data
throughout the process, the process still belongs to the blocking state. '' '
So, in the non-blocking IO, the user process is actually a need to constantly ask about kernel data ready or not.

Examples :

 1 import socket
 2 
 3 sk = socket.socket()
 4 sk.bind(('127.0.0.1', 8888))
 5 sk.listen(5)
 6 sk.setblocking(False)
 7 
 8 conn_list = []
 9 del_list = []
10 while 1:
11 
12     try:
13         conn, addr = sk.accept()  # 此处不再阻塞
14         conn_list.append(conn)
15 
16     except BlockingIOError:
17         if conn_list:
18             print(conn_list)
19 
20         for conn in conn_list:
21             try:
22                 msg = conn.recv(1024).decode('utf-8') # 此处不再阻塞
23                 if not msg:
24                     del_list.append(conn)
25                     continue
26                 conn.send(b'hi')
27             except BlockingIOError:
28                 pass
29             except ConnectionResetError:
30                 del_list.append(conn)
31 
32         for c in del_list:
33             conn_list.remove(c)
34             c.close()
35 
36         del_list.clear()
37 
38     # sk.close()
View Code
However, non-blocking IO model is not recommended. 
We can not otherwise advantages: the ability to do other live (including the submission of other tasks, that is, the "background" can have multiple tasks, "" while "" execution) waiting for task completion time.
But also conceal its shortcomings:
1. Cycle call recv () will significantly push up CPU utilization; this is also a reason time.sleep (2) We remain in the code, otherwise the card machine in the case of very prone to low with the host 
2. Mission accomplished response delay is increased, since each period of time before a read operation to a polling, and the task may be completed at any time between polls. This will lead to lower overall data throughput.

Further, in this embodiment recv () functions is more sense 'operation completion "role, the actual operating system provides a more efficient detection" operation is finished, "that interfaces, 
for example, select () multiplexed use mode, can detect a plurality of connections is active.

IO multiplexing to achieve the three API (select, poll and epoll) differences and connections
select, mechanisms poll, epoll IO are multiplexed, I / O multiplexing is achieved by a mechanism to monitor a plurality of descriptors, descriptor once a ready (typically a read 
-ready or write-ready) , the application can be notified accordingly read and write operations. But they are synchronous I / O the select, poll, epoll essence, because they need to read and write in the event
when ready responsible for their own reading and writing, that the reading and writing process is blocked, and asynchronous I / O is no need to own responsible for reading and writing, asynchronous I / O implementation responsible for copying the data from the kernel
to the user space. Three prototype is as follows:
1 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
2 
3 int poll(struct pollfd *fds, nfds_t nfds, int timeout);
4 
5 int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
All three models have a different IO multiplexing of different platforms to support, but do not support epoll under windows, but fortunately we have selectors module, to help us select the current default under the most appropriate platform
 1 # 服务器端
 2 
 3 import selectors
 4 from socket import *
 5 
 6 
 7 def accept(sk, mask):
 8     conn, addr = sk.accept()
 9     sel.register(conn, selectors.EVENT_READ, read)
10 
11 
12 def read(conn, mask):
13     try:
14         msg = conn.recv(1024)
15         if not msg:
16             print('closing connection:', conn)
17             sel.unregister(conn)
18             conn.close()
19             return
20         conn.send(b'good bye')
21     except Exception:
22             print('closing connection:', conn)
23             sel.unregister(conn)
24             conn.close()
25 
26 sk = socket()
27 sk.bind(('127.0.0.1', 8888))
28 sk.listen(5)
29 
30 ## Set socket interface is non-blocking 
31 is  sk.setblocking (False)
 32  # choose a suitable mechanism multiplexed I IO 
33 is SEL = selectors.DefaultSelector ()
 34 is  
35  sel.register (SK, selectors.EVENT_READ, Accept )
 36  '' ' corresponding to the read list to select an append sk's object, and accept a callback function to bind
 37 [  it means if someone sk connection request, accept method is called ' '' 
38 is  
39  the while . 1 :
 40      = sel.select Events () # detect all sk, conn, wait data for the completion stage 
41 is      for sel_obj, mask in Events:
 42 is          the callback = sel_obj.data   # the callback = Read
43 is          the callback (sel_obj.fileobj, mask)   # Read (SK, mask) 
44 is  
45  
46 is  # previous example common client 
47  Import Socket
 48  
49 SK = socket.socket ()
 50 sk.connect (( ' 127.0.0.1 ' , 8888 ))
 51 is  
52 is  the try :
 53 is      the while . 1 :
 54 is          sk.send (B ' Hello ' )
 55          Print (sk.recv (1024) .decode ( ' UTF-. 8 ' ))
 56 is         ipt = input('>>>').encode('utf-8')
57         sk.send(ipt)
58 except:
59     sk.close()
View Code

 

3_ multiplexing IO (IO multiplexing)

 

 

 

 1 # 服务器端
 2 
 3 import socket
 4 import select
 5 
 6 sk = socket.socket()
 7 sk.bind(('127.0.0.1', 8888))
 8 sk.listen(5)
 9 
10 sk.setblocking(False)
11 
12 read_list = [sk]
13 
14 while 1:
15 
16     r_lst, w_lst, x_lst = select.select(read_list, [], [])
17 
18     for r in r_lst:
19         if r is sk:
20             conn, addr = r.accept()
21             read_list.append(conn)
22         else:
23             ret = r.recv(1024).decode('utf-8')
24             if ret == '':
25                 r.close()
26                 read_list.remove(r)
27                 continue
28             print(ret)
29             r.send(b'goodbye')
30 
31 # 客户端
32 
33 import socket
34 
35 sk = socket.socket()
36 sk.connect(('127.0.0.1', 8888))
37 
38 try:
39     while 1:
40         sk.send(b'hello')
41         print(sk.recv(1024).decode('utf-8'))
42         ipt = input('>>>').encode('utf-8')
43         sk.send(ipt)
44 except:
45     sk.close()

 

 
 

Guess you like

Origin www.cnblogs.com/funyou/p/12178836.html