Документация Python неофициальный перевод

timerfd.md

227 строк · 11.6 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3/howto/timerfd.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# HOWTO по файловому дескриптору таймера89**Версия:**10111.131213В этом HOWTO рассматривается поддержка в Python файлового дескриптора таймера Linux.1415## Примеры1617В следующем примере показано, как использовать файловый дескриптор таймера для выполнения функции два раза в секунду:1819```python20# Практические скрипты должны использовать неблокирующий таймер,21# здесь для простоты мы используем блокирующий таймер.22import os, time2324# Создаём файловый дескриптор таймера25fd = os.timerfd_create(time.CLOCK_REALTIME)2627# Запускаем таймер через 1 секунду с интервалом в полсекунды.28os.timerfd_settime(fd, initial=1, interval=0.5)2930try:31    # Обрабатываем события таймера четыре раза.32    for _ in range(4):33        # read() будет блокироваться, пока таймер не сработает.34        _ = os.read(fd, 8)35        print("Timer expired")36finally:37    # Не забудьте закрыть файловый дескриптор таймера!38    os.close(fd)39```4041Чтобы избежать потери точности, вызванной типом [`float`](https://python-all.ru/3/library/functions.html#float), файловые дескрипторы таймера позволяют задавать начальное время срабатывания и интервал в целых наносекундах с помощью вариантов функций `_ns`.4243Этот пример показывает, как [`epoll()`](https://python-all.ru/3/library/select.html#select.epoll) можно использовать с файловыми дескрипторами таймера для ожидания, пока файловый дескриптор не будет готов к чтению:4445```python46import os, time, select, socket, sys4748# Создаём объект epoll.49ep = select.epoll()5051# В этом примере используется адрес обратной петли для отправки команды "stop" на сервер.52#53# $ telnet 127.0.0.1 123454# Пытаемся подключиться к 127.0.0.1...55# Подключено к 127.0.0.1.56# Экранирующий символ – '^]'.57# стоп58# Соединение закрыто внешним хостом.59#60sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)61sock.bind(("127.0.0.1", 1234))62sock.setblocking(False)63sock.listen(1)64ep.register(sock, select.EPOLLIN)6566# Создавать файловые дескрипторы таймера в неблокирующем режиме.67num = 368fds = []69for _ in range(num):70    fd = os.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)71    fds.append(fd)72    # Зарегистрировать файловый дескриптор таймера для событий чтения73    ep.register(fd, select.EPOLLIN)7475# Запустить таймер с помощью os.timerfd_settime_ns() в наносекундах.76# Таймер 1 срабатывает каждые 0.25 секунд, таймер 2 каждые 0.5 секунд и т.д.77for i, fd in enumerate(fds, start=1):78    one_sec_in_nsec = 10**979    i = i * one_sec_in_nsec80    os.timerfd_settime_ns(fd, initial=i//4, interval=i//4)8182timeout = 383try:84    conn = None85    is_active = True86    while is_active:87        # Ожидать истечения таймера в течение 3 секунд.88        # epoll.poll() возвращает список пар (fd, событие).89        # fd – это файловый дескриптор.90        # sock и conn[=возвращаемое значение socket.accept()] – это объекты сокетов, а не файловые дескрипторы.91        # Поэтому используйте sock.fileno() и conn.fileno() для получения файловых дескрипторов.92        events = ep.poll(timeout)9394        # Если несколько файловых дескрипторов таймера готовы к чтению одновременно,95        # epoll.poll() возвращает список пар (fd, событие).96        #97        # В данном примере настроек:98        #    1-й таймер срабатывает каждые 0,25 секунды через 0,25 секунды. (0,25; 0,5; 0,75; 1,0; ...)99        #    2-й таймер – каждые 0,5 секунды через 0,5 секунды. (0,5; 1,0; 1,5; 2,0; ...)100        #    3-й таймер – каждые 0,75 секунды через 0,75 секунды. (0,75; 1,5; 2,25; 3,0; ...)101        #102        #    Через 0,25 секунды срабатывает только 1-й таймер.103        #    Через 0,5 секунды 1-й и 2-й таймеры срабатывают одновременно.104        #    Через 0,75 секунды 1-й и 3-й таймеры срабатывают одновременно.105        #    Через 1,5 секунды 1-й, 2-й и 3-й таймеры срабатывают одновременно.106        #107        # Если файловый дескриптор таймера был сигнализирован более одного раза с момента108        # последнего вызова os.read(), os.read() возвращает количество сигналов109        # в виде целого числа в порядке хоста (байты).110        print(f"Signaled events={events}")111        for fd, event in events:112            if event & select.EPOLLIN:113                if fd == sock.fileno():114                    # Проверить, есть ли запрос на подключение.115                    print(f"Accepting connection {fd}")116                    conn, addr = sock.accept()117                    conn.setblocking(False)118                    print(f"Accepted connection {conn} from {addr}")119                    ep.register(conn, select.EPOLLIN)120                elif conn and fd == conn.fileno():121                    # Проверить, есть ли данные для чтения.122                    print(f"Reading data {fd}")123                    data = conn.recv(1024)124                    if data:125                        # Для безопасности следует перехватывать исключение UnicodeDecodeError.126                        cmd = data.decode()127                        if cmd.startswith("stop"):128                            print(f"Stopping server")129                            is_active = False130                        else:131                            print(f"Unknown command: {cmd}")132                    else:133                        # Данных больше нет, закрыть соединение.134                        print(f"Closing connection {fd}")135                        ep.unregister(conn)136                        conn.close()137                        conn = None138                elif fd in fds:139                    print(f"Reading timer {fd}")140                    count = int.from_bytes(os.read(fd, 8), byteorder=sys.byteorder)141                    print(f"Timer {fds.index(fd) + 1} expired {count} times")142                else:143                    print(f"Unknown file descriptor {fd}")144finally:145    for fd in fds:146        ep.unregister(fd)147        os.close(fd)148    ep.close()149```150151Этот пример показывает, как [`select()`](https://python-all.ru/3/library/select.html#select.select) можно использовать с файловыми дескрипторами таймера для ожидания, пока файловый дескриптор не будет готов к чтению:152153```python154import os, time, select, socket, sys155156# В этом примере используется адрес обратной петли для отправки команды "stop" на сервер.157#158# $ telnet 127.0.0.1 1234159# Пытаемся подключиться к 127.0.0.1...160# Подключено к 127.0.0.1.161# Экранирующий символ – '^]'.162# стоп163# Соединение закрыто внешним хостом.164#165sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)166sock.bind(("127.0.0.1", 1234))167sock.setblocking(False)168sock.listen(1)169170# Создавать файловые дескрипторы таймера в неблокирующем режиме.171num = 3172fds = [os.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)173       for _ in range(num)]174select_fds = fds + [sock]175176# Запускать таймеры с помощью os.timerfd_settime() в секундах.177# Таймер 1 срабатывает каждые 0.25 секунд, таймер 2 каждые 0.5 секунд и т.д.178for i, fd in enumerate(fds, start=1):179   os.timerfd_settime(fd, initial=i/4, interval=i/4)180181timeout = 3182try:183    conn = None184    is_active = True185    while is_active:186       # Ожидать истечения таймера в течение 3 секунд.187       # select.select() возвращает список файловых дескрипторов или объектов.188       rfd, wfd, xfd = select.select(select_fds, select_fds, select_fds, timeout)189       for fd in rfd:190           if fd == sock:191               # Проверить, есть ли запрос на подключение.192               print(f"Accepting connection {fd}")193               conn, addr = sock.accept()194               conn.setblocking(False)195               print(f"Accepted connection {conn} from {addr}")196               select_fds.append(conn)197           elif conn and fd == conn:198               # Проверить, есть ли данные для чтения.199               print(f"Reading data {fd}")200               data = conn.recv(1024)201               if data:202                   # Для безопасности следует перехватывать исключение UnicodeDecodeError.203                   cmd = data.decode()204                   if cmd.startswith("stop"):205                       print(f"Stopping server")206                       is_active = False207                   else:208                       print(f"Unknown command: {cmd}")209               else:210                   # Данных больше нет, закрыть соединение.211                   print(f"Closing connection {fd}")212                   select_fds.remove(conn)213                   conn.close()214                   conn = None215           elif fd in fds:216               print(f"Reading timer {fd}")217               count = int.from_bytes(os.read(fd, 8), byteorder=sys.byteorder)218               print(f"Timer {fds.index(fd) + 1} expired {count} times")219           else:220               print(f"Unknown file descriptor {fd}")221finally:222    for fd in fds:223       os.close(fd)224    sock.close()225    sock = None226```227