10

私の問題を説明するコードは次のとおりです。

def blocking1():
    while True:
        yield 'first blocking function example'

def blocking2():
    while True:
        yield 'second blocking function example'

for i in blocking1():
    print 'this will be shown'

for i in blocking2():
    print 'this will not be shown'

while Trueループを含む 2 つの関数があります。これらはデータを生成し、それをどこかに記録します (ほとんどの場合、sqlite データベースに)。

私はスレッドで遊んでいて、それが機能するようになりました。しかし、私はそれがあまり好きではありません...私がやりたいことは、ブロック機能を非同期にすることです。何かのようなもの:

def blocking1(callback):
    while True:
        callback('first blocking function example')

def blocking2(callback):
    while True:
        callback('second blocking function example')

def log(data):
    print data

blocking1(log)
blocking2(log)

Pythonでこれを達成するにはどうすればよいですか? 標準ライブラリには非同期コアが付属しており、このゲームのビッグネームは Twisted ですが、これらは両方ともソケット IO に使用されているようです。

ソケットに関連しないブロッキング関数を非同期にするにはどうすればよいですか?

4

5 に答える 5

30

ブロッキング関数は、戻らないが、プロセスをアイドル状態のままにして、それ以上の作業を完了できない関数です。

あなたは私たちにあなたのブロッキング機能を非ブロッキングにするように求めています。ただし、オペレーティングシステムを作成している場合を除きブロック機能はありません。ブロックするシステムコールを呼び出すためにブロックする関数がある場合もあれば、多くの計算を行うために「ブロックする」関数がある場合もあります。

前者のタイプの関数を非ブロッキングにすることは、基礎となるシステムコールを非ブロッキングにすることなしには不可能です。そのシステムコールが何であるかによっては、プログラムにイベントループを追加せずに非ブロッキングにするのは難しい場合があります。呼び出しを行ってブロックしないようにするだけでなく、別の呼び出しを行って、その呼び出しの結果が関連付け可能な場所に配信されることを確認する必要があります。

この質問への答えは、非常に長いPythonプログラムと、さまざまなOSインターフェイスとその動作についての多くの説明ですが、幸いなことに、私はすでにその答えを別のサイトに書いています。私はそれをツイストと呼んだ。あなたの特定のタスクがツイステッドリアクターによってすでにサポートされている場合、あなたは幸運です。それ以外の場合は、タスクが既存のオペレーティングシステムの概念に対応している限り、reactorを拡張してそれをサポートできます。実際には、これらのメカニズムは2つしかありません。これまでのすべての実用的なオペレーティングシステムのファイル記述子と、WindowsのI/O完了ポートです。

他のケースでは、関数が大量のCPUを消費しているために戻ってこない場合、関数は実際にはブロックされていません。あなたのプロセスはまだ順調に進んでいて、仕事を成し遂げています。これに対処するには、次の3つの方法があります。

  • 別のスレッド
  • 個別のプロセス
  • イベントループがある場合は、何らかの作業を行うようにタスクを記述して、定期的に生成されるタスクを記述し、他のタスクを実行できるようにするために、近い将来にイベントループを再開するように要求します。

Twistedでは、この最後のテクニックはさまざまな方法で実行できますが、構文的に便利なトリックを使用すると、次のように簡単に実行できます。

from twisted.internet import reactor
from twisted.internet.task import deferLater
from twisted.internet.defer import inlineCallbacks, returnValue

@inlineCallbacks
def slowButSteady():
    result = SomeResult()
    for something in somethingElse:
        result.workHardForAMoment(something)
        yield deferLater(reactor, 0, lambda : None)
    returnValue(result)
于 2011-02-12T03:40:33.770 に答える
12

協調的なマルチタスクにジェネレーターを使用できますが、ジェネレーター間で制御を渡す独自のメイン ループを作成する必要があります。

上記の例を使用した(非常に単純な)例を次に示します。

def blocking1():
    while True:
        yield 'first blocking function example'

def blocking2():
    while True:
        yield 'second blocking function example'


tasks = [blocking1(), blocking2()]

# Repeat until all tasks have stopped
while tasks:
    # Iterate through all current tasks. Use
    # tasks[:] to copy the list because we
    # might mutate it.
    for t in tasks[:]:
        try:
            print t.next()
        except StopIteration:
            # If the generator stops, remove it from the task list
            tasks.remove(t)

ジェネレーターが新しいジェネレーターを生成できるようにすることで、さらに改善することができます。これはタスクに追加できますが、うまくいけば、この単純化された例で一般的なアイデアが得られます。

于 2011-02-11T07:06:57.070 に答える
2

ねじれたフレームワークは、単なるソケットではありません。サブプロセスとの対話など、多くのシナリオに対応する非同期アダプターがあります。それをよく見てみることをお勧めします。それはあなたがやろうとしていることをします。

于 2011-02-11T07:41:57.450 に答える
1

完全な OS スレッドを使用したくない場合は、「マイクロスレッド」を含む多くの興味深い機能を追加する Python のバリアントであるStacklessを試すことができます。参考になる良い例がたくさんあります。

于 2011-02-11T06:17:01.257 に答える