2010-11-26 4 views
3

저는 여러 명령을 실행 중입니다.이 명령은 Python 2.6을 실행하는 Linux 컴퓨터에서 병렬로 시간이 걸릴 수 있습니다.파이썬 subprocess.Popen & communicate()의 멀티 스레드 사용이 개선 되었습니까?

따라서 여러 개의 명령 그룹을 병렬 처리하고 실행 후 즉시 출력을 캡처하려면 subprocess.Popen 클래스와 process.communicate() 메서드를 사용했습니다.

def run_commands(commands, print_lock): 
    # this part runs in parallel. 
    outputs = [] 
    for command in commands: 
     proc = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) 
     output, unused_err = proc.communicate() # buffers the output 
     retcode = proc.poll()     # ensures subprocess termination 
     outputs.append(output) 
    with print_lock: # print them at once (synchronized) 
     for output in outputs: 
      for line in output.splitlines(): 
       print(line) 

은 다른 곳에서 다음과 같이라고 :

processes = [] 
print_lock = Lock() 
for ...: 
    commands = ... # a group of commands is generated, which takes some time. 
    processes.append(Thread(target=run_commands, args=(commands, print_lock))) 
    processes[-1].start() 
for p in processes: p.join() 
print('done.') 

예상되는 결과는 명령 그룹의 각 출력은 한 번에 실행이 병렬로 수행하는 동안 표시되는 것입니다.

그러나 두 번째 출력 그룹 (물론 두 번째 스레드는 예약 비결정성으로 인해 변경됩니다)에서 줄 바꿈없이 각 줄에 인쇄 된 문자 수만큼 공간을 추가하기 시작합니다. 입력 에코가 꺼져 있습니다 - 터미널 상태가 "왜곡"또는 "크래시"입니다. (reset 쉘 명령을 실행하면 정상으로 복원됩니다.)

처음에는 '\r'의 처리 이유를 찾으려했지만 그 이유가 아닙니다. 내 코드에서 볼 수 있듯이 splitlines()을 사용하여 올바르게 처리했으며 repr() 기능을 출력에 적용했음을 확인했습니다.

이유는 stdout/stderr에 대해 Popencommunicate()에 파이프를 동시 사용하는 것입니다. 파이썬 2.7에서 단축키 메서드 check_output을 시도했지만 성공하지 못했습니다. 물론 모든 명령 실행 및 인쇄를 직렬화하면 위에 설명한 문제가 발생하지 않습니다.

Popencommunicate()을 병렬로 처리하는 더 좋은 방법이 있습니까?

+1

'.communicate()'는'Popen.wait()'을 호출하여 프로세스가 완료되도록합니다. 그러나'proc.poll()'은 프로세스 완료를 보장하지 않습니다. 프로세스가 완료되지 않은 경우'None'을 리턴합니다. 예를 들어 리눅스에서는'waitpid (pid, WNOHANG)'를 호출합니다. 'WNOHANG'을위한 문서에서는 : "waitpid() 함수는 상태가 pid에 의해 지정된 자식 프로세스 중 하나에 즉시 사용 가능하지 않으면 호출 스레드의 실행을 일시 중단하지 않습니다." http://www.mkssoftware.com/docs/man3/waitpid.3.asp – jfs

+1

모든 프로세스를 병렬로 실행하고 그룹 내의 순서를 유지하는 프로세스 그룹별로 출력하는 스크립트는 다음과 같습니다. https://gist.github.com/717467 – jfs

+0

Sebastian에 감사드립니다. 그러나 솔루션으로 문제가 완전히 해결되지 않았습니다. 파이썬의 서브 프로세스 구현과 동기화에서 버그로 보입니다. 최종 결과를 답으로 추가했습니다. – Achimnol

답변

2

최종 결과는 J.F.Sebastian의 의견에서 영감을 얻었습니다.

http://bitbucket.org/daybreaker/kaist-cs443/src/247f9ecf3cee/tools/manage.py

파이썬 버그 것 같다. 부분적으로 "/R"의 문제를 해결하기

for line in output.splitlines(): 

; 당신의 예제 코드에서

+0

124 번 줄에서'None'을'group_id'로 대체하고'time.sleep (0.5)'를 제거 할 수 있습니다. 'print_stdout()'은 어떤 프로세스 시작/실행/종료 후에도 queue.put ((FLAG_GROUP_SIZE, group_id, len (command_group))'이 호출 될 수 있다는 가정하에 쓰여졌습니다. 당신이 122 행에서 언급 한 파이썬 버그가 어떻게 터미널에서 "왜곡 된"출력과 관련이 있는지 이해하십시오. 제 수정의 아이디어는 주 스레드에서 프로세스를 시작/대기하는 것입니다 .IO threads 및't.join()' 또 다른 스레드에서 실행될 수 있으며, 단순화를 위해 주 스레드에 남겨 두었습니다. – jfs

+0

btw, outputs [group_id] [command_id]'는 주어진 명령에 대해 * all * 출력을 포함해야하므로'output_queue.put (프로세스 당 한 줄 이상의 출력을 보려는 경우'read_stdout()'의'group_id, command_id, line))'이 유효하지 않습니다. – jfs

+0

예, 아이디어가 주/스레드와'queue.put ((FLAG_GROUP_SIZSE, ...))'는 프로세스 실행 중에 언제든지 호출 될 수 있습니다. 그러나'time.sleep'을 넣지 않으면 무작위로 왜곡 된 출력을 보여줍니다. – Achimnol

0

run_commands가 실제로 수행해야하는 것은 확실하지 않지만 단순히 서브 프로세스에서 폴링을 수행하고 리턴 코드를 무시하고 루프를 계속 수행하는 것처럼 보입니다. 출력물을 인쇄 할 부분에 도달하면 하위 프로세스가 완료되었음을 어떻게 알 수 있습니까?

+0

'poll()'메쏘드에 의해 보장됩니다. 리턴 코드가 이미 사용 가능하면 (즉,'communicate()'중에 서브 프로세스가 끝났음을 의미) 또는 완료 될 때까지 기다리는 경우 즉시 반환됩니다. – Achimnol

+0

이렇게 차이는 없습니다. - " Popen.poll() - 하위 프로세스가 종료되었는지 확인하십시오. returncode 속성을 설정하고 반환하십시오." AND "Popen.wait() - 자식 프로세스가 종료 될 때까지 기다렸다가 returncode 특성을 설정하고 반환합니다." ? – Satwik

+0

파이썬 매뉴얼에'wait()'가'communicate()'다음에 호출되면 교착 상태를 일으킨다 고합니다. 내 질문의 포인트는 반환 코드 또는 하위 프로세스 종료가 아닙니다. 다중 스레드 하위 프로세스는 먼저 컨트롤이있는 첫 번째 스레드가 완료된 후 터미널 출력을 비 웁니다. – Achimnol

0

나는의 사용을주의

for line in output.splitlines(True): 

의 사용이 도움이되었습니다.