2009-06-15 7 views
2

特定の機能を完了するために長い時間がかかる背景関数呼び出しで私のコードのハンドルが戻るには長い時間がかかることを確認します。戻り値は必要ありません。遅い関数が返る前に、スクリプトの次のコード行を実行したいと思います。より正確には、関数はUSBを介して別のシステムに(SWIGを使用したC++ライブラリ経由で)コマンドを送信し、他のシステムがタスクを完了すると「OK」値を返します。次の例で問題を再現しました。どのようにして "tic"と "toc"の印刷を遅らせることができますか?スレッドには解決策が含まれていると思いますが、あまりにも慣れていません。誰も私にこの問題を解決する簡単な方法を教えてもらえますか?は私のコードで

from math import sqrt 
from time import sleep 

def longcalc(): 
    total = 1e6 
    for i in range(total): 
     r = sqrt(i) 
    return r 

def longtime(): 
    #Do stuff here 
    sleep(1) 
    return "sleep done" 

print "tic" 
longcalc() 
print "toc" 
longtime() 
print "tic" 

答えて

4

長い遅延の前にGIL(グローバルインタープリタロック)を解放してPythonに戻す前に再取得するようにSWIGed C++コードを特別に設定していない限り、マルチスレッドは実際には非常に有用ではないかもしれません。代わりにmultiprocessingを試みることができる:

from multiprocessing import Process 

if __name__ == '__main__': 
    print "tic" 
    Process(target=longcalc).start() 
    print "toc" 
    Process(target=longtime).start() 
    print "tic" 

マルチプロセッシングは、Python 2.6で標準ライブラリに以降ですが、別途downloadedこととバージョン2.5および2.4のためにインストールすることができます。

編集:アスカーはこれ以上複雑な何かをしようとして当然のものであり、コメントで説明します。 「を」 "私はで終わるエラーの束を取得:"pickle.PicklingError: Can't pickle <type 'PySwigObject'>: it's not found as __builtin__.PySwigObject"これはすべて私のコードを再編成することなく、解決することができます。 ?プロセスがボタンにバインドされたメソッドの内側からwxPythonインターフェイスに呼び出されました。 "" "

multiprocessingプロセスの境界を越えてオブジェクトをピクルする必要があります。あなたはそれをシリアライズし、デシリアライズする方法を見つける、そしてcopy_reg moduleでそれを登録することができない限り、正確にここに関与しているオブジェクトをSWIGgedが、わからない、あなたが境界を越えて、それを渡す避けるために必要がある(単一によって所有され、使用さSWIGgedオブジェクトを作ります特に、__main__にmodule-globalオブジェクトとして持っていない、SWIGgedオブジェクトを含んでいないオブジェクトを介してQueue.Queueでプロセス間で通信するなど)。

エラー(実際には "ending with"と表示されているものと異なる場合があります)が実際にはより重要なのかもしれませんが、私はそれらを見ずに推測することはできません。、残念ながら

import time 

def work(): 
    for x in range(3): 
     time.sleep(1) 
     print 'Tick...' 
    print 'Done!' 
    return 'Result!' 

def main(): 
    print 'Starting up...' 
    f = Future(work) 
    print 'Doing more main thread work...' 
    time.sleep(1.5) 
    print 'Now waiting...' 
    print 'Got result: %s' % f.wait() 

+1

Dave BeazlyはGILのプレゼンテーションで、I/O中にGILがリリースされていると述べています(また、I/Oについて話し、USB情報を待っています)。現在の状況:反対の情報を与えている2つのアーチファミン主義者。私は今何をすべきですか? – bayer

+0

あなたはGILに関してC++コードが何をしているのかまだ分かりません。あなたはそれがI/Oをし、GILをリリースするかどうかわからない場合、あなたは知らない。これは矛盾したアドバイスではありません。これは「あなたが何かを知らないときに何をすべきか」というアドバイスです。わからないときはマルチプロセッシングを使用してください。 –

+0

@ S.Lottは正しい:I/O Beazleyの話は、Pythonの標準ライブラリを介して行われたものである - 神秘的なSWIGged C++ライブラリは協力するかもしれないし、協力しないかもしれない。 [私が知っていることについて知っている唯一の文書は、SWIGでのGILハンドリングについて - http://matt.eifelle.com/2007/11/23/enabling-thread-support-in-swig-and-python/を参照してください。 well-documented feature ;-)]] –

1
from threading import Thread 
# ... your code  

calcthread = Thread(target=longcalc) 
timethread = Thread(target=longtime) 

print "tic" 
calcthread.start() 
print "toc" 
timethread.start() 
print "tic" 

Pythonでマルチスレッドの詳細については、python threading docsを見てください。

マルチスレッドに関する警告の言葉:それは難しいことができます。 非常にです。マルチスレッドソフトウェアのデバッグは、ソフトウェア開発者としての最悪の経験につながります。あなたが潜在的なデッドロックと競合状態の世界に掘り下げる前に

だから、それはansynchronousなものにあなたの同期USBの相互作用を変換することは理にかなっていることを絶対に確認してください。具体的には、非同期コードに依存するコードが完了した後に実行されるようにしてください(callback methodまたは類似のものを介して)。

0

あなたが標準ライブラリに含まれていない未来を、使用することができますが、非常にシンプルに実装する:

from threading import Thread, Event 

class Future(object): 
    def __init__(self, thunk): 
     self._thunk = thunk 
     self._event = Event() 
     self._result = None 
     self._failed = None 
     Thread(target=self._run).start() 

    def _run(self): 
     try: 
      self._result = self._thunk() 
     except Exception, e: 
      self._failed = True 
      self._result = e 
     else: 
      self._failed = False 
     self._event.set() 

    def wait(self): 
     self._event.wait() 
     if self._failed: 
      raise self._result 
     else: 
      return self._result 

あなたはこのように、この特定の実装を使用します「メイン」スレッドを持たないシステムを使用する場合、「待機」を呼び出すタイミングを判断することは困難です。あなたは明らかに答えが必要になるまで、明らかに処理を止めたくありません。

Twistedでは、deferToThreadを使用すると、メインループに戻ることができます。それが終了していますときに実際に原子炉と終了を起動するために、あなたにもこれを実行する必要があると思いますが、

import time 

from twisted.internet import reactor 
from twisted.internet.task import deferLater 
from twisted.internet.threads import deferToThread 
from twisted.internet.defer import inlineCallbacks 

def work(): 
    for x in range(3): 
     time.sleep(1) 
     print 'Tick...' 
    print 'Done!' 
    return 'Result!' 

@inlineCallbacks 
def main(): 
    print 'Starting up...' 
    d = deferToThread(work) 
    print 'Doing more main thread work...' 
    yield deferLater(reactor, 1.5, lambda : None) 
    print "Now 'waiting'..." 
    print 'Got result: %s' % (yield d) 

::ツイストで慣用的に同等のコードは、このようなものになるだろう

reactor.callWhenRunning(
    lambda : main().addCallback(lambda _: reactor.stop())) 
reactor.run() 

Twistedとの主な違いは、メインスレッドで他のタイムスタンプが発生すると、他のネットワーク接続がトラフィックを獲得し、GUIでボタンがクリックされます。その作業はシームレスに行われます。deferLateryield dは実際にはスレッド全体を停止する、彼らは "メイン"を一時停止するinlineCallbacksコルーチン。