Læs fil linje for linje med asyncio

Jeg vil gerne læse flere log-filer, som de er skrevet, og behandle deres input med asyncio. Koden vil have til at køre på windows. Fra hvad jeg forstår, at søge rundt omkring begge stackoverflow og web, asynkron fil I/O er tricky på de fleste operativsystemer (select vil ikke virke efter hensigten, for eksempel). Mens jeg er sikker på, at jeg kunne gøre det sammen med andre metoder (fx tråde), jeg selv, at jeg ville prøve asyncio at se, hvad det er. Den mest nyttige svar ville nok være en, der beskriver, hvad “arkitektur” af en løsning til dette problem skal se ud, altså hvor forskellige funktioner og coroutines skal være foretaget eller planlagt.

Følgende giver mig en generator, der læser de filer, linje-for-linje (gennem afstemningsstederne, hvilket er acceptabelt):

import time

def line_reader(f):
    while True:
        line = f.readline()
        if not line:
            time.sleep(POLL_INTERVAL)
            continue
        process_line(line)

Med flere filer til at overvåge og behandle denne form for kode, der ville kræve tråde. Jeg har ændret det lidt til at være mere brugbart med 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)

Denne slags fungerer, når jeg planlægge det gennem asyncio event loop, men hvis process_data blokke, så er det selvfølgelig ikke godt. Når du starter ud, troede jeg, at den løsning ville se noget som

def process_data():
    ...
    while True:
        ...
        line = yield from line_reader()
        ...

men jeg kunne ikke finde ud af, hvordan man gør sit arbejde (i det mindste ikke uden process_data styring af en hel del af staten).

Nogen ideer om hvordan jeg skal strukturere denne form for kode?

Jeg har testet den øverste version af koden, og som er i stand til at læse ændringer af filen.

OriginalForfatteren josteinb | 2015-11-20

4 svar

  1. 15

    Fra hvad jeg forstår, at søge rundt omkring begge stackoverflow og web, asynkron fil I/O er tricky på de fleste operativsystemer (vælg ikke vil virke efter hensigten, for eksempel). Mens jeg er sikker på, at jeg kunne gøre det sammen med andre metoder (fx tråde), jeg selv, at jeg ville prøve asyncio at se, hvad det er.

    asyncio er select baseret på *nix-systemer under kølerhjelmen, så du vil ikke være i stand til at gøre ikke-blokering af fil I/O uden brug af tråde. På Windows, asyncio kan bruge IOCP, der støtter ikke-blokering af fil I/O, men dette er ikke understøttet af asyncio.

    Din kode er fint, bortset fra, at du bør gøre blokerende i/O-opkald i tråde, så at du ikke blokerer event loop, hvis i/O er langsom. Heldigvis, det er virkelig simpelt at off load arbejde tråde hjælp loop.run_in_executor funktion.

    Først opsætte en dedikeret tråd-pool til din I/O:

    from concurrent.futures import ThreadPoolExecutor
    io_pool_exc = ThreadPoolExecutor()

    Og så bare losse nogen blokerende I/O opkald til eksekutor:

    ...
    line = yield from loop.run_in_executor(io_pool_exc, f.readline)
    ...

    OriginalForfatteren Jashandeep Sohi

  2. 21

    Hjælp aiofiles:

    async with aiofiles.open('filename', mode='r') as f:
        async for line in f:
            print(line)

    REDIGER 1

    Som @Jashandeep nævnt, at du bør bekymre sig om blokering af operationer:

    En anden metode er select og eller epoll:

    from select import select
    
    files_to_read, files_to_write, exceptions = select([f1, f2], [f1, f2], [f1, f2], timeout=.1)

    Den timeout parameter er vigtig her.

    se: https://docs.python.org/3/library/select.html#select.select

    EDIT 2

    Kan du registrere en fil til læse – /skrive med: loop.add_reader()

    Det bruger interne EPOLL Handler inde i løkken.

    REDIGER 3

    Men husk Epoll vil ikke arbejde med almindelige filer.

    OriginalForfatteren pylover

  3. 3

    Din kode struktur ser godt til mig, følgende kode kører fint på min maskine:

    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())
    Det ser godt ud! Jeg havde glemt, at jeg skal åbne filen i den funktion, du har navngivet test, og der løser min hovedpine. Tak!
    Jeg tror, at det er vildledende lidt – du er faktisk ikke til at skubbe den læser sig selv. Du bare gør det sleep asynkront. IIUC.
    Du har fuldstændig ret. Svaret var ganske enkelt om fastsættelse OP kode. Måske vil jeg slette det.

    OriginalForfatteren Vincent

  4. -1

    asyncio ikke understøtter fil operationer endnu, undskyld.

    Således det kan ikke hjælpe med dit problem.

    Ja, men for at være mere konstruktiv, jeg kan godkende den metode: loop.add_reader (Deling EPOLL handler indeni loop) er nok til at læse en fil luns efter luns uden at blokere hele tråden, og opnå den maksimale ydeevne, Så jeg er uenig med dig, Python understøtter læsning i asyncio måde, med nogle begrænsninger.
    EPOLL understøtter ikke almindelige filer på Linux

    OriginalForfatteren Andrew Svetlov

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *