2014-01-17 43 views
10

次の単純なスクリプトはサブプロセスでハングします。断続的に呼び出します(約30%)。
use_lock = Trueでない限り、それがハングしないので、サブプロセスがスレッドセーフではないと私を導いてくれます! 予想される動作は、スクリプトが5〜6秒以内に終了することです。
バグを示すには、 "python bugProof.py"を何回か実行してハングアップしてください。 Ctrl-Cが終了します。ポポポンは1回か2回しか現れませんが、3回目は現れません。subprocess.Popenはスレッドセーフではありませんか?

#!/bin/sh 
echo "Calculating factorial (anything that's somewhat compute intensive, this script takes 3 sec on my machine" 
ans=1 
counter=0 
fact=999 
while [ $fact -ne $counter ] 
do 
    counter=`expr $counter + 1` 
    ans=`expr $ans \* $counter` 
done 
echo "Factorial calculation done" 
read -p "Test input (this part is critical for bug to occur): " buf 
echo "$buf" 

システム情報: Linuxの2.6.32-358.123.2.openstack.el6.x86_64#1 SMP木9月(factorial.sh)上で参照

import subprocess, threading, fcntl, os, time 
end_time = time.time()+5 
lock = threading.Lock() 
use_lock = False 
path_to_factorial = os.path.join(os.path.dirname(os.path.realpath(__file__)),'factorial.sh') 

def testFunction(): 
    print threading.current_thread().name, '| pre-Popen' 
    if use_lock: lock.acquire() 
    p = subprocess.Popen([path_to_factorial], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
    if use_lock: lock.release() 
    print threading.current_thread().name, '| post-Popen' 
    fcntl.fcntl(p.stdout, fcntl.F_SETFL, os.O_NONBLOCK) 
    fcntl.fcntl(p.stderr, fcntl.F_SETFL, os.O_NONBLOCK) 
    while time.time()<end_time: 
     try: p.stdout.read() 
     except: pass 
     try: p.stderr.read() 
     except: pass 
    print threading.current_thread().name, '| DONE' 

for i in range(3): 
    threading.Thread(target=testFunction).start() 


シェルスクリプト26 17:14:58 EDT 2013 x86_64 x86_64 x86_64 GNU/Linux
Python 2.7.3(デフォルト、Jan 22 2013、11:34:30)
[GCC 4.4.6 20120305(Red Hat 4.4.6-4) ] on linux2

+0

Popenコールでclose_fds = Trueを設定すると、何らかの理由で問題が解決されます。 – Roman

+0

これは文字通り、私が最近Pythonで遭遇した最も怒っていたことでした。すごくイライラしてる。 –

答えて

13

Python 2.xでは、subprocess.Popenに影響するさまざまな競合条件があります。 (たとえば、2.7では、さまざまなタイミングの問題を防ぐために&がガベージコレクションを復元するが、これはスレッドセーフではありません)。例えば、 http://bugs.python.org/issue2320,http://bugs.python.org/issue1336およびhttp://bugs.python.org/issue14548があります。

サブプロセスの実質的な改訂は、fork(fork)とexecの間の重要な部分に合理的に関与するPythonコードを実行するのではなく、これらの問題を解決するためのものです(特に&フォークはCモジュールにあります) subprocess32モジュールの最新のPython 2.xリリースでバックポートされています。 PyPIページから次の点に注意してください。「POSIXシステムでは、スレッドアプリケーションで使用すると信頼性が高いことが保証されています。

私は上記のコードのクラッシュを時折(私にとっては約25%)再現できますが、import subprocess32 as subprocessを使用した後では100回以上の実行で何の失敗も見られませんでした。

subprocess32(とPython 3.2+)はデフォルトでclose_fds = Trueに設定されていますが、subprocess32を使用すると、close_fds = Falseであってもエラーは発生しませんでした。

関連する問題