Je souhaite lire plusieurs fichiers journaux au fur et à mesure qu'ils sont écrits et traiter leur entrée avec asyncio. Le code devra s'exécuter sur Windows. D'après ce que je comprends de la recherche à la fois sur stackoverflow et sur le Web, les E/S de fichiers asynchrones sont difficiles sur la plupart des systèmes d'exploitation (select
ne fonctionnera pas comme prévu, par exemple). Bien que je sois sûr que je pourrais le faire avec d'autres méthodes (par exemple des threads), je pensais que j'essaierais asyncio pour voir à quoi cela ressemble. La réponse la plus utile serait probablement celle qui décrit à quoi devrait ressembler "l'architecture" d'une solution à ce problème, c'est-à-dire comment différentes fonctions et coroutines doivent être appelées ou planifiées.
Ce qui suit me donne un générateur qui lit les fichiers ligne par ligne (par interrogation, ce qui est acceptable):
import time
def line_reader(f):
while True:
line = f.readline()
if not line:
time.sleep(POLL_INTERVAL)
continue
process_line(line)
Avec plusieurs fichiers à surveiller et à traiter, ce type de code nécessiterait des threads. Je l'ai légèrement modifié pour être plus utilisable avec asyncio:
import asyncio
def line_reader(f):
while True:
line = f.readline()
if not line:
yield from asyncio.sleep(POLL_INTERVAL)
continue
process_line(line)
Ce genre de travail lorsque je le planifie via la boucle d'événement asyncio, mais si process_data
blocs, alors ce n'est bien sûr pas bon. Au début, j'ai imaginé que la solution ressemblerait à quelque chose comme
def process_data():
...
while True:
...
line = yield from line_reader()
...
mais je ne pouvais pas comprendre comment faire ce travail (du moins pas sans process_data
gérer un peu d'état).
Des idées sur la façon de structurer ce type de code?
D'après ce que je comprends de la recherche à la fois sur stackoverflow et sur le Web, les E/S de fichiers asynchrones sont difficiles sur la plupart des systèmes d'exploitation (select ne fonctionnera pas comme prévu, par exemple). Bien que je sois sûr que je pourrais le faire avec d'autres méthodes (par exemple des threads), je pensais que j'essaierais asyncio pour voir à quoi cela ressemble.
asyncio
est select
basé sur des systèmes * nix sous le capot, vous ne pourrez donc pas faire non -blocage des E/S de fichiers sans utiliser de threads. Sous Windows, asyncio
peut utiliser IOCP , qui prend en charge les E/S de fichiers non bloquants, mais cela n'est pas pris en charge par asyncio
.
Votre code est correct, sauf que vous devez bloquer les appels d'E/S dans les threads, afin de ne pas bloquer la boucle d'événements si les E/S sont lentes. Heureusement, il est très simple de décharger le travail sur les threads en utilisant le loop.run_in_executor
une fonction.
Tout d'abord, configurez un pool de threads dédié pour vos E/S:
from concurrent.futures import ThreadPoolExecutor
io_pool_exc = ThreadPoolExecutor()
Ensuite, déchargez simplement les appels d'E/S bloquants vers l'exécuteur:
...
line = yield from loop.run_in_executor(io_pool_exc, f.readline)
...
Utilisation des aiofiles :
async with aiofiles.open('filename', mode='r') as f:
async for line in f:
print(line)
EDIT 1
Comme l'a mentionné @Jashandeep, vous devez vous soucier des opérations de blocage:
Une autre méthode est select
et ou epoll
:
from select import select
files_to_read, files_to_write, exceptions = select([f1, f2], [f1, f2], [f1, f2], timeout=.1)
Le paramètre timeout
est important ici.
voir: https://docs.python.org/3/library/select.html#select.select
EDIT 2
Vous pouvez enregistrer un fichier en lecture/écriture avec: loop.add_reader ()
Il utilise le gestionnaire EPOLL interne à l'intérieur de la boucle.
EDIT 3
Mais rappelez-vous que l'Epoll ne fonctionnera pas avec des fichiers normaux.
Votre structure de code me semble bonne, le code suivant fonctionne bien sur ma machine:
import asyncio
PERIOD = 0.5
@asyncio.coroutine
def readline(f):
while True:
data = f.readline()
if data:
return data
yield from asyncio.sleep(PERIOD)
@asyncio.coroutine
def test():
with open('test.txt') as f:
while True:
line = yield from readline(f)
print('Got: {!r}'.format(line))
loop = asyncio.get_event_loop()
loop.run_until_complete(test())
asyncio
ne prend pas encore en charge les opérations sur les fichiers, désolé.
Ainsi, il ne peut pas résoudre votre problème.