2014-01-17 3 views
10

Следующий простой скрипт зависает подпроцессом. Периодически вызывайте вызов (примерно 30% времени).
Если use_lock = True, а затем он никогда не зависает, что приводит к тому, что подпроцесс не является потокобезопасным! Ожидаемое поведение - завершение скриптов в течение 5-6 секунд.
Чтобы продемонстрировать ошибку, просто запустите «python bugProof.py» несколько раз, пока она не зависает. Ctrl-C выйдет. Вы увидите, что «post-Popen» появляется только один или два раза, но не в третий раз.Подпроцесс.

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() 


Скрипт ссылки выше (factorial.sh):

#!/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 Чт сен 26 17:14:58 EDT 2013 x86_64 x86_64 x86_64 GNU/Linux
Python 2.7.3 (по умолчанию, 22 января 2013 г., 11:34:30)
[GCC 4.4.6 20120305 (Red Hat 4.4.6-4) ] на linux2

+0

Помещение close_fds = True в вызове Popen также устраняет проблему по какой-либо причине. – Roman

+0

Это была буквально самая бескровная вещь, с которой я столкнулся в Python в последнее время. Соу разочаровывает. –

ответ

13

На Python 2.x существуют различные условия гонки, влияющие на подпроцесс. (например, на 2,7 он отключает &, восстанавливает сборку мусора, чтобы предотвратить различные проблемы с синхронизацией, но это само по себе не является потокобезопасным). См. http://bugs.python.org/issue2320, http://bugs.python.org/issue1336 и http://bugs.python.org/issue14548 для некоторых вопросов в этой области.

Существенный пересмотр подпроцесса был сделан в Python 3.2, который обращается к ним (среди прочего, код exk & exec находится в модуле C, вместо того, чтобы делать некоторый разумно задействованный код Python в критической части между fork и exec) , и он доступен для последних версий Python 2.x в модуле subprocess32. Обратите внимание на страницу PyPI: «В системах POSIX гарантируется надежность при использовании в поточных приложениях».

Я могу воспроизвести случайные (около 25% для меня) сбой кода выше, но после использования import subprocess32 as subprocess я не видел никаких отказов в 100+ пробегах.

Обратите внимание, что subprocess32 (и Python 3.2+) по умолчанию close_fds = True, но с подпроцессом32 на месте я не видел сбоев даже с close_fds = False (не то, что вам в целом нужно).

Смежные вопросы