Вы можете использовать селекторы для ручки I/O мультиплексирование:
https://docs.python.org/3/library/selectors.html
Попробуйте это:
#! /usr/bin/python3
import sys
import fcntl
import os
import selectors
# set sys.stdin non-blocking
orig_fl = fcntl.fcntl(sys.stdin, fcntl.F_GETFL)
fcntl.fcntl(sys.stdin, fcntl.F_SETFL, orig_fl | os.O_NONBLOCK)
# function to be called when enter is pressed
def got_keyboard_data(stdin):
print('Keyboard input: {}'.format(stdin.read()))
# register event
m_selector = selectors.DefaultSelector()
m_selector.register(sys.stdin, selectors.EVENT_READ, got_keyboard_data)
while True:
sys.stdout.write('Type something and hit enter: ')
sys.stdout.flush()
for k, mask in m_selector.select():
callback = k.data
callback(k.fileobj)
Приведенный выше код будет держать на линии
for k, mask in m_selector.select():
, пока не произойдет зарегистрированное событие, возвращая selector_key экземпляр (к) и маску отслеживаемых событий.
В приведенном выше примере мы зарегистрировали только одно событие (Enter нажатие клавиши):
m_selector.register(sys.stdin, selectors.EVENT_READ, got_keyboard_data)
Селектор ключа экземпляра определяется следующим образом:
abstractmethod register(fileobj, events, data=None)
Таким образом, метод регистра устанавливает к.данные, как наша функция обратного вызова got_keyboard_data, и называет его, когда Enter нажата клавиша:
callback = k.data
callback(k.fileobj)
Более полный пример (и, надеюсь, более полезным) будет мультиплекс STDIN данные пользователя с Incomming соединений от сети:
import selectors
import socket
import sys
import os
import fcntl
m_selector = selectors.DefaultSelector()
# set sys.stdin non-blocking
def set_input_nonblocking():
orig_fl = fcntl.fcntl(sys.stdin, fcntl.F_GETFL)
fcntl.fcntl(sys.stdin, fcntl.F_SETFL, orig_fl | os.O_NONBLOCK)
def create_socket(port, max_conn):
server_addr = ('localhost', port)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.setblocking(False)
server.bind(server_addr)
server.listen(max_conn)
return server
def read(conn, mask):
global GO_ON
client_address = conn.getpeername()
data = conn.recv(1024)
print('Got {} from {}'.format(data, client_address))
if not data:
GO_ON = False
def accept(sock, mask):
new_conn, addr = sock.accept()
new_conn.setblocking(False)
print('Accepting connection from {}'.format(addr))
m_selector.register(new_conn, selectors.EVENT_READ, read)
def quit():
global GO_ON
print('Exiting...')
GO_ON = False
def from_keyboard(arg1, arg2):
line = arg1.read()
if line == 'quit\n':
quit()
else:
print('User input: {}'.format(line))
GO_ON = True
set_input_nonblocking()
# listen to port 10000, at most 10 connections
server = create_socket(10000, 10)
m_selector.register(server, selectors.EVENT_READ, accept)
m_selector.register(sys.stdin, selectors.EVENT_READ, from_keyboard)
while GO_ON:
sys.stdout.write('>>> ')
sys.stdout.flush()
for k, mask in m_selector.select():
callback = k.data
callback(k.fileobj, mask)
# unregister events
m_selector.unregister(sys.stdin)
# close connection
server.shutdown()
server.close()
# close select
m_selector.close()
Вы можете протестировать использование двух терминалов. первый терминал:
$ python3 test.py
>>> bla
открыт другой терминал и запустить:
$ nc localhost 10000
hey!
назад к первому
>>> qwerqwer
Результат (видно на главном терминале):
$ python3 test.py
>>> bla
User input: bla
>>> Accepting connection from ('127.0.0.1', 39598)
>>> Got b'hey!\n' from ('127.0.0.1', 39598)
>>> qwerqwer
User input: qwerqwer
>>>
[возможно] (http://stackoverflow.com/questions/ 8416586/turn-off-buffering) a [duplicate] (http://stackoverflow.com/questions/107705/python-output-buffering) –
Неблокирование на stdin либо не работает, либо не работает очень надежно. Разрешено ли использование потоков/многопроцессорности? Причина, которая должна работать –