我无法理解 python 中的轮询/选择

发布于 2024-12-05 09:58:17 字数 144 浏览 0 评论 0原文

我正在 python 中使用 UDP 进行一些线程异步网络实验。

我想了解轮询和 select python 模块,我从未在 C/C++ 中使用过它们。

那些是做什么用的?我有点理解一点选择,但是在观看资源时它会阻塞吗?民意调查的目的是什么?

I'm doing some threaded asynchronous networking experiment in python, using UDP.

I'd like to understand polling and the select python module, I've never used them in C/C++.

What are those for ? I kind of understand a little select, but does it block while watching a resource ? What is the purpose of polling ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

时光病人 2024-12-12 09:58:17

好的,一次一个问题。

那些是做什么用的?

这是一个简单的套接字服务器框架:

s_sock = socket.socket()
s_sock.bind()
s_sock.listen()

while True:
    c_sock, c_addr = s_sock.accept()
    process_client_sock(c_sock, c_addr)

服务器将循环并接受来自客户端的连接,然后调用其处理函数与客户端套接字进行通信。这里有一个问题:process_client_sock可能需要很长时间,甚至包含一个循环(通常是这种情况)

def process_client_sock(c_sock, c_addr):
    while True:
        receive_or_send_data(c_sock)

在这种情况下,服务器无法接受更多连接。

一个简单的解决方案是使用多进程或多线程,只需创建一个新线程来处理请求,而主循环继续侦听新连接。

s_sock = socket.socket()
s_sock.bind()
s_sock.listen()

while True:
    c_sock, c_addr = s_sock.accept()
    thread = Thread(target=process_client_sock, args=(c_sock, c_addr))
    thread.start()

这当然有效,但考虑到性能还不够好。由于新进程/线程需要额外的 CPU 和内存,因此不空闲的服务器可能会获得数千个连接。

所以selectpoll系统调用试图解决这个问题。您给 select 一组文件描述符,并告诉它在任何 fd 准备好读/写/或发生异常时通知您。

它(选择)在观看资源时会阻塞吗?

是或否取决于您传递给它的参数。

正如select man page所说,它将获得struct timeval参数

int select(int nfds, fd_set *readfds, fd_set *writefds,
       fd_set *exceptfds, struct timeval *timeout);

struct timeval {
long    tv_sec;         /* seconds */
long    tv_usec;        /* microseconds */
};

分为三种情况:

  1. timeout.tv_sec == 0 和 timeout.tv_usec = 0

    无阻塞,立即返回

  2. timeout == NULL

    永远阻塞,直到文件描述符准备好。

  3. 超时是正常的

    等待一定时间,如果仍然没有文件描述符可用,则超时并返回。

民意调查的目的是什么?

简单来说就是:轮询在等待 IO 时释放 CPU 来进行其他工作

这是基于一个简单的事实:

  1. CPU 比 IO 快得多
  2. 等待 IO 是浪费时间,因为大多数时间,CPU 都会空闲

希望它有帮助。

Okay, one question a time.

What are those for?

Here is a simple socket server skeleton:

s_sock = socket.socket()
s_sock.bind()
s_sock.listen()

while True:
    c_sock, c_addr = s_sock.accept()
    process_client_sock(c_sock, c_addr)

Server will loop and accept connection from a client, then call its process function to communicate with client socket. There is a problem here: process_client_sock might takes a long time, or even contains a loop(which is often the case).

def process_client_sock(c_sock, c_addr):
    while True:
        receive_or_send_data(c_sock)

In which case, the server is unable to accept any more connections.

A simple solution would be using multi-process or multi-thread, just create a new thread to deal with request, while the main loop keeps listening on new connections.

s_sock = socket.socket()
s_sock.bind()
s_sock.listen()

while True:
    c_sock, c_addr = s_sock.accept()
    thread = Thread(target=process_client_sock, args=(c_sock, c_addr))
    thread.start()

This works of course, but not well enough considering performance. Because new process/thread takes extra CPU and memory, not idle for servers might get thousands connections.

So select and poll system calls tries to solve this problem. You give select a set of file descriptors and tell it to notify you if any fd is ready to read/write/ or exception happens.

does it(select) block while watching a resource?

Yes, or no depends on the parameter you passed to it.

As select man page says, it will get struct timeval parameter

int select(int nfds, fd_set *readfds, fd_set *writefds,
       fd_set *exceptfds, struct timeval *timeout);

struct timeval {
long    tv_sec;         /* seconds */
long    tv_usec;        /* microseconds */
};

There are three cases:

  1. timeout.tv_sec == 0 and timeout.tv_usec = 0

    No-blocking, return immediately

  2. timeout == NULL

    block forever until a file descriptor is ready.

  3. timeout is normal

    wait for certain time, if still no file descriptor is available, timeout and return.

What is the purpose of polling ?

Put it into simple words: polling frees CPU for other works when waiting for IO.

This is based on the simple facts that

  1. CPU is way more faster than IO
  2. waiting for IO is a waste of time, because for the most time, CPU will be idle

Hope it helps.

━╋う一瞬間旳綻放 2024-12-12 09:58:17

如果您执行readrecv,则您只等待一个连接。如果有多个连接,则必须创建多个进程或线程,浪费系统资源。

使用 selectpollepoll,您可以仅用一个线程监视多个连接,并在其中任何一个有可用数据时收到通知,并且然后您在相应的连接上调用 readrecv

它可能会无限阻塞、阻塞给定时间或根本不阻塞,具体取决于参数。

If you do read or recv, you're waiting on only one connection. If you have multiple connections, you will have to create multiple processes or threads, a waste of system resource.

With select or poll or epoll, you can monitor multiple connections with only one thread, and get notified when any of them has data available, and then you call read or recv on the corresponding connection.

It may block infinitely, block for a given time, or not block at all, depending on the arguments.

一袭水袖舞倾城 2024-12-12 09:58:17

select() 接受 3 个套接字列表来检查三个条件(读、写、错误),然后返回(通常更短,通常为空)实际准备好处理这些条件的套接字列表。

s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.bind((Local_IP, Port1))
s1.listen(5)

s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2.bind((Local_IP, Port2))
s2.listen(5)

sockets_that_might_be_ready_to_read = [s1,s2]
sockets_that_might_be_ready_to_write_to = [s1,s2]
sockets_that_might_have_errors = [s1,s2]


([ready_to_read], [ready_to_write], [has_errors])  = 
       select.select([sockets_that_might_be_ready_to_read],
                     [sockets_that_might_be_ready_to_write_to], 
                     [sockets_that_might_have_errors],            timeout)


for sock in ready_to_read:
    c,a = sock.accept()
    data = sock.recv(128)
    ...
for sock in ready_to_write:
    #process writes
    ...
for sock in has_errors:
    #process errors

因此,如果套接字在等待超时秒数后没有尝试连接,则列表 read_to_read 将为空 - 此时accept()和recv()是否会阻塞并不重要 - 它们不会因空而被调用list...

如果一个套接字已准备好读取,那么它将有数据,因此它也不会阻塞。

select() takes in 3 lists of sockets to check for three conditions (read, write, error), then returns (usually shorter, often empty) lists of sockets that actually are ready to be processed for those conditions.

s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.bind((Local_IP, Port1))
s1.listen(5)

s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2.bind((Local_IP, Port2))
s2.listen(5)

sockets_that_might_be_ready_to_read = [s1,s2]
sockets_that_might_be_ready_to_write_to = [s1,s2]
sockets_that_might_have_errors = [s1,s2]


([ready_to_read], [ready_to_write], [has_errors])  = 
       select.select([sockets_that_might_be_ready_to_read],
                     [sockets_that_might_be_ready_to_write_to], 
                     [sockets_that_might_have_errors],            timeout)


for sock in ready_to_read:
    c,a = sock.accept()
    data = sock.recv(128)
    ...
for sock in ready_to_write:
    #process writes
    ...
for sock in has_errors:
    #process errors

So if a socket has no attempted connections after waiting timeout seconds, then the list ready_to_read will be empty - at which point it doesn't matter if the accept() and recv() would block - they won't get called for the empty list....

If a socket is ready to read, then if will have data, so it won't block then, either.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文