Я не могу понять опрос / выбор в Python - PullRequest
11 голосов
/ 18 сентября 2011

Я провожу эксперимент с многопоточной асинхронной сетью в python, используя UDP.

Я хотел бы понять опрос и модуль select python, я никогда не использовал их в C / C ++.

Для чего они? Я вроде как немного выбираю, но блокирует ли он при просмотре ресурса? Какова цель опроса?

Ответы [ 3 ]

10 голосов
/ 28 марта 2015

Хорошо, один вопрос за раз.

Для чего они?

Вот простой скелет сервера сокетов:

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()

Это работает, конечно, но недостаточно хорошо, учитывая производительность. Поскольку новый процесс / поток требует дополнительной загрузки ЦП и памяти, серверы, не работающие в режиме ожидания, могут получить тысячи соединений.

Итак, системные вызовы select и poll пытаются решить эту проблему. Вы даете select набор файловых дескрипторов и говорите ему, чтобы он уведомлял вас, готов ли какой-либо fd к чтению / записи / или происходит исключение.

блокирует ли он (выбирает) при просмотре ресурса?

Да или нет, зависит от параметра, который вы передали ему.

Как выбрать справочную страницу говорит, он получит 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. тайм-аут == NULL

    блокировать навсегда, пока дескриптор файла не будет готов.

  3. время ожидания нормальное

    ждать определенное время, если дескриптор файла по-прежнему недоступен, время ожидания и возврат.

Какова цель опроса?

Проще говоря: опрос освобождает процессор для других работ при ожидании ввода-вывода .

Это основано на простых фактах, которые

  1. Процессор намного быстрее, чем IO
  2. ожидание ввода-вывода - пустая трата времени, поскольку в большинстве случаев процессор будет простаивать

Надеюсь, это поможет.

9 голосов
/ 18 сентября 2011

Если вы делаете read или recv, вы ожидаете только одно соединение. Если у вас есть несколько соединений, вам придется создать несколько процессов или потоков, что является пустой тратой системного ресурса.

С помощью select или poll или epoll вы можете отслеживать несколько соединений только с одним потоком и получать уведомления, когда на любом из них есть данные, а затем вы вызываете read или recv на соответствующее соединение.

Может зависеть бесконечно, блокировать на заданное время или не блокировать вообще, в зависимости от аргументов.

2 голосов
/ 13 декабря 2013

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

Так что, если у сокета нет попыток подключений по истечении времени ожидания, то список ready_to_read будет пустым - в этот момент не имеет значения, блокируют ли accept () и recv () - они не будут вызваны для пустого списка ....

Если сокет готов к чтению, то если он будет иметь данные, значит, он также не будет блокироваться.

...