いくつかのログファイルが書き込まれているときに読み取り、その入力をasyncioで処理したいと思います。コードはWindowsで実行する必要があります。 stackoverflowとWebの両方を検索して理解したことから、非同期ファイルI/Oはほとんどのオペレーティングシステムで注意が必要です(たとえば、select
は意図したとおりに動作しません)。他のメソッド(スレッドなど)でこれを行うことができると確信していますが、asyncioを試して、それがどのようなものかを確認します。最も役立つ答えは、おそらく、この問題の解決策の「アーキテクチャ」がどのように見えるべきか、つまり異なる機能やコルーチンがどのように呼び出されるか、またはスケジュールされるべきかを説明するものでしょう。
以下は、ファイルを1行ずつ読み取るジェネレーターを提供します(ポーリングを通じて、許容されます)。
import time
def line_reader(f):
while True:
line = f.readline()
if not line:
time.sleep(POLL_INTERVAL)
continue
process_line(line)
監視および処理するファイルがいくつかあるため、この種のコードにはスレッドが必要です。私は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)
この種の機能は、asyncioイベントループを介してスケジュールすると機能しますが、process_data
ブロック、それはもちろん良くありません。始めたとき、私は解決策が次のようになると想像しました
def process_data():
...
while True:
...
line = yield from line_reader()
...
しかし、私はその作業を行う方法を理解できませんでした(少なくともprocess_data
かなりの状態を管理します)。
この種のコードをどのように構成すべきかについてのアイデアはありますか?
StackoverflowとWebの両方を検索して理解したことから、非同期ファイルI/Oはほとんどのオペレーティングシステムで扱いにくい(たとえば、selectは意図したとおりに機能しません)。他のメソッド(スレッドなど)でこれを行うことができると確信していますが、asyncioを試して、それがどのようなものかを確認します。
asyncio
isselect
は内部の* nixシステムに基づいているため、非実行はできません。 -スレッドを使用せずにファイルI/Oをブロックします。 Windowsでは、asyncio
は [〜#〜] iocp [〜#〜] を使用できます。これは非ブロッキングファイルI/Oをサポートしますが、これはasyncio
。
I/Oが遅い場合にイベントループをブロックしないように、スレッドでI/O呼び出しをブロックする必要があることを除いて、コードは問題ありません。幸いなことに、loop.run_in_executor
関数を使用してスレッドへの作業をオフロードするのは本当に簡単です。
まず、I/O専用のスレッドプールをセットアップします。
from concurrent.futures import ThreadPoolExecutor
io_pool_exc = ThreadPoolExecutor()
そして、executorへのブロッキングI/O呼び出しをオフロードするだけです。
...
line = yield from loop.run_in_executor(io_pool_exc, f.readline)
...
aiofiles を使用:
async with aiofiles.open('filename', mode='r') as f:
async for line in f:
print(line)
EDIT 1
@Jashandeepが言及したように、ブロック操作に注意する必要があります。
別の方法は、select
またはepoll
です。
from select import select
files_to_read, files_to_write, exceptions = select([f1, f2], [f1, f2], [f1, f2], timeout=.1)
ここでは、timeout
パラメーターが重要です。
参照: https://docs.python.org/3/library/select.html#select.select
EDIT 2
loop.add_reader() を使用して、読み取り/書き込み用のファイルを登録できます。
ループ内で内部EPOLLハンドラーを使用します。
EDIT 3
ただし、通常のファイルではEpollは機能しないことに注意してください。
あなたのコード構造は私にとって見栄えがよく、次のコードは私のマシンで問題なく動作します:
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
はまだファイル操作をサポートしていません。申し訳ありません。
したがって、問題を解決することはできません。