2010-12-28 2 views
1

세 개의 셸 스크립트 P1, P2 및 P3이 연결되어 있습니다. 이 세 개의 쉘 스크립트는 직렬로 실행해야하지만, 주어진 시간에 여러 개의 P1 및 P2와 P3이 실행될 수 있습니다.Queue, threading.Thread 및 subprocess가 너무 희박한 멀티 쓰레드 파이썬 스크립트가 왜

나는 이것을 수십 개의 파일에서 빠르게 실행해야하므로 스레드를 사용하고 병렬로 작업해야합니다.

저는 이것을 달성하기 위해 파이썬 스레드, 대기열 및 서브 프로세스 모듈을 사용하고 있습니다.

내 문제는 하나 이상의 스레드 수가있을 때 프로그램이 비정상적으로 작동하고 스레드가 재생 가능한 방식으로 서로 손을 놓지 않는다는 것입니다. 모두 다섯 스레드가 완벽하게 작동하고 완료 작업.

이것은 스레드를 사용하여 무언가를하는 첫 번째 시도이며 이것이 경쟁 조건과 관련된 스레드의 일반적인 문제로 인한 것임을 확신합니다. 하지만 어떻게 코드를 정리할 수 있는지 알고 싶습니다.

실제 코드는 (https://github.com/harijay/xtaltools/blob/master/process_multi.py)입니다. 가짜 코드는 아래와 같습니다. 죄송합니다 코드가 지저분한 경우.

내 질문에 왜 내가이 디자인을 사용하여 비정상적인 행동을합니까? 스레드는 주어진 시간에 모두 다른 파일에 액세스합니다. 또한 subprocess.call은 쉘 스크립트가 완료되고 생성 된 파일이 디스크에 기록 될 때만 반환됩니다.

어떻게해야합니까? 가능한 한 간결하게 설계를 설명하려고 노력했습니다.

내 기본 설계 :

P1_Queue = Queue() 
P2_Queue = Queue() 
P3_Queue = Queue() 

class P1_Thread(Thread): 
    def __init__(self,P1_Queue,P2_Queue): 
     Thread.__init__(self) 
     self.in_queue = P1_Queue 
     self.out_queue = P2_Queue 

    def run(self): 
     while True: 
      my_file_to_process = self.in_queue.get() 
      if my_file_to_process = None: 
       break 
      P1_runner = P1_Runner(my_file_to_process) 
      P1_runner.run_p1_using_subprocess() 
      self.out_queue.put(my_file_to_process) 

클래스 P1 러너는 다음 입력 파일 핸들을 받아 파일 입력을 사용하고 사용하여 새 출력 파일을 생성하는 쉘 스크립트를 실행하는 subprocess.call()를 호출 run_p1_using_subprocess 메소드.

class P1_runner(object): 

    def __init__(self,inputfile): 
     self.my_shell_script = """#!/usr/bin/sh 
prog_name <<eof 
input 1 
... 
eof""" 
     self.my_shell_script_file = open("some_unique_p1_file_name.sh") 
     os.chmod("some_unique_file_name.sh",0755) 

    def run_p1_using_subprocess(self): 
     subprocess.call([self.my_shell_script_file]) 

I have essentially similar classes for P2 and P3 . All of which call a shell script that is custom generated 

The chaining is achieved using a series of Thread Pools. 
p1_worker_list = [] 
p2_worker_list = [] 
p3_worker_list = [] 

for i in range(THREAD_COUNT): 
    p1_worker = P1_Thread(P1_Queue,P2_Queue) 
    p1_worker.start() 
    p1_worker_list.append(p1_worker) 

for worker in p1_worker_list: 
    worker.join() 

And then again the same code block for p2 and p3 

for i in range(THREAD_COUNT): 
    p2_worker = P2_Thread(P2_Queue,P3_Queue) 
    p2_worker.start() 
    p2_worker_list.append(p1_worker) 

for worker in p2_worker_list: 
    worker.join() 

도와 주셔서 감사합니다/조언을 톤 스레드의 종료 조건이 다른 스레드가 입력 큐를 비 웁니다 때 그들이 자살을하게

+0

이것은 귀하의 코드가 아닙니다. 'if my_file_to_process = None :'은 구문 오류입니다. – Falmarri

+0

죄송합니다. 설명을위한 코드였습니다. 구문 오류를 수정했습니다. – harijay

답변

2

음이 정말 나쁜 :

runner.run() 

혹시 수동으로 스레드의 run 메소드를 호출하지 않아야합니다. .start()를 사용하여 스레드를 시작합니다. 귀하의 코드는 큰 혼란이며 아무도 여기를 빠져 나와 오류를 찾을 수 없습니다.

+0

포인터를 보내 주셔서 감사합니다. 그러나 이것은 정확히 내가 찾고있는 종류의 제안입니다. – harijay

+0

감사합니다 Falmarri 및 Apalala. 나는 코드를 많이 정리했다. https://github.com/harijay/auriga. 이제 OSX에서 원활하게 작동합니다. 나는 여전히 "OSError : [Errno 26] Text file busy"를 subprocess.check_call()과 함께 무작위로 얻는다. 나는 그것을 동등한 Popen 호출로 대체하려고했지만 우분투에서만 항상 오류가 발생합니다. – harijay

+0

@Downvoter : -1을 설명하는 것이 중요합니까? – Falmarri

1

: 그들은 'didn를하기 때문에

my_file_to_process = self.in_queue.get() 
    if my_file_to_process = None: # my sister ate faster than I did, so... 
     break # ... I kill myself! 

스레드는 죽어있다 그들이 더 준비가되었을 때해야 할 일을 찾는다.

대신 입력 대기열의 이벤트가 신호 될 때까지 스레드를 대기 (대기)로 설정하고, 오케 스트레이터 (주 프로그램)가 처리가 완료되었음을 알리는 경우에만 다이를 대기 (자살 플래그 설정 및 모든 대기열).

(이미 코드가 변경된 것을 확인합니다.)

@Falmarri 아마 다른 곳에서 자신의 노트에 의미 귀하의 질문에 (뭔가 다른 사람이 대답 할 수있는) 코드에서 threading 라이브러리의 전체 사용은 프로그램의 사용 잘못하고 있기 때문에 특정 문제에 대해 아니라고 무엇

언어는 일반적으로 어색합니다.예를 들면 :

  • worker.join()에 대한 호출 따라서, P2 스레드를 시작 동시성에서 어떤 시도를 물리 치고 전에 순서대로 모든 P1 스레드의 종료를위한 주요 프로그램 대기한다.
  • Thread.run()을 무시하거나 생성자에 대해 호출 가능하도록 제공해야합니다. Pn_runner 클래스는 필요하지 않습니다.
  • 모든 스레드 클래스는 동일합니다. 프로세스 단계마다 다른 클래스가 필요하지 않습니다.
  • 이미 파이썬을 사용하고 있다면, 순수한 파이썬에서 쉽게 작업을 수행 할 수 없다면 외부 프로그램 (훨씬 적은 쉘 스크립트)을 호출하는 것은 의미가 없습니다.
  • 위의 이유로 파일 시스템에 쉘 스크립트를 작성하는 것은 매우 이상하고 거의 불필요합니다.

내가 당신이 특정 문제를 해결하기 위해 수행하는 것이 좋습니다 것은 :

  1. 시도는 100 % 파이썬에 충실. 당신이 할 수 없거나 너무 어려워 보인다면 적어도 외부에서 액세스해야하는 특정 기능을 발견했을 것입니다.
  2. 동시성을 사용하지 않는 솔루션을 빌드하십시오.
  3. 프로그램의 성능을 측정하고 알고리즘을 향상 시키십시오.
  4. 가능한 경우 스레딩을 피하십시오. CPU 바인딩 된 프로그램은 스레딩없이 사용 가능한 모든 사이클을 먹습니다. 너무 디스크 바인딩 된 (또는 외부/원격 리소스에 바인드 된) 프로그램은 디스크가 없을 경우 디스크를 기다리고 있습니다. 스레드 스레딩의 이점을 얻으려면 계산과 외부 리소스 사용간에 올바른 균형이 이루어져야합니다. 그렇지 않으면 사용량이 많은 경우에도 요청을 처리 할 수 ​​있어야합니다.
  5. pythonic 길을 간단하게 시작하고 점차 복잡한 기능과 복잡성을 증가시키면서 언제나 복잡해 보이는 것을 피하십시오.

파이썬에서 스레딩에 대해 가르쳐 주려는 의도가 있다면 꼭 실험 해 볼 간단한 문제를 찾으십시오. 그리고 만약 당신이 원하는 것이 병렬로 여러개의 쉘 스크립트를 돌리는 것이라면, bash과 다른 쉘들은 이미 그것을위한 조항을 가지고 있으며 파이썬을 사용할 필요가 없습니다.

+0

스레드를 잠자기 상태로 설정하는 방법을 모릅니다. 또한 스레드가 자동으로 주위에 붙어 있습니까? – harijay

+0

my_file_to_process = 없음 : sleep (sleep_time) 이면 충분하지만 순진합니다. 저는 파이썬 라이브러리를 직접 배우고 있습니다. 이미 필요한 부분이 있다는 것을 이미 알고 있습니다. mutex 표준 라이브러리를보십시오. 스레드 안전 데이터 구조는 동시 모니터를 작성하지 않는다는 것을 기억하십시오. – Apalala

+0

http://docs.python.org/library/threading.html도 참조하십시오. – Apalala

관련 문제