2017-09-20 1 views
1

파일 목록에서 파일을 복사하는 10 개의 스레드를 생성하는 다음 코드가 있습니다. 나는 파일의 다른 목록에 대해 이것을 반복해서 호출하고 있는데, 나는 fileQueue가 종료되면 쓰레드가 죽어가는 것 같지 않다는 것을 알았다. 코드가 오랜 작업으로 느려지는 것처럼 보였다. 스레드 내에서 충돌이 발생하여 "스레드 Thread-45 예외 :"가 표시되기 시작했습니다. 스레드 그냥 깔끔하게 코드의 호출 사이에 죽어없는 이유 파이썬 스레드가 죽지 않는다

import Queue, threading 
from PyQt4 import QtCore, QtGui 
import shutil 

fileQueue = Queue.Queue() 

class ThreadedCopy: 
    totalFiles = 0 
    copyCount = 0 
    lock = threading.Lock() 

    def __init__(self, inputList, progressBar=False): 
     self.totalFiles = len(inputList) 

     print str(self.totalFiles) + " files to copy." 

     if progressBar: 
      progressBar = QtGui.QProgressDialog("Copying files...", "Cancel", 0, self.totalFiles) 
      progressBar.setMinimumDuration(0) 
      progressBar.setWindowModality(QtCore.Qt.WindowModal) 
      self.threadWorkerCopy(inputList, progressBar) 
     else: 
      self.threadWorkerCopy(inputList) 


    def CopyWorker(self, progressBar): 
     while True: 
      fileName = fileQueue.get() 
      shutil.copy(fileName[0], fileName[1]) 
      fileQueue.task_done() 
      with self.lock: 
       self.copyCount += 1 
       if not progressBar: 
        print str(self.copyCount) + "of" + str(self.totalFiles) 
        percent = (self.copyCount * 100)/self.totalFiles 
        print "File copy: " + str(percent) + "%" 
       else: 
        progressBar.setValue(self.copyCount) 


    def threadWorkerCopy(self, fileNameList, progressBar=False): 
     threadCount = 10 
     for i in range(threadCount): 
      t = threading.Thread(target=self.CopyWorker, args=(progressBar,)) 
      t.daemon = True 
      t.start() 
     for fileName in fileNameList: 
      fileQueue.put(fileName) 

     fileQueue.join() 

사람이 알고 있나요

:

여기 내 코드는 매우 깨끗하고 간단 설명서를 읽는 내가 아는 모든 것을에서입니까? fileQueue가 없어지면 일단 조용히 죽어야한다는 것을 이해하는 것으로부터!

편집 : 여기 당신은 아마 모든 스레드 .join 전화를 잊었 고정 코드

import Queue, threading 
from PyQt4 import QtCore, QtGui 
import shutil 


fileQueue = Queue.Queue() 

class ThreadedCopy: 
    totalFiles = 0 
    copyCount = 0 
    lock = threading.Lock() 

    def __init__(self, inputList, progressBar=False): 
     self.totalFiles = len(inputList) 

     print str(self.totalFiles) + " files to copy." 

     if progressBar: 
      progressBar = QtGui.QProgressDialog("Copying files...", "Cancel", 0, self.totalFiles) 
      progressBar.setMinimumDuration(0) 
      progressBar.setWindowModality(QtCore.Qt.WindowModal) 
      self.threadWorkerCopy(inputList, progressBar) 
     else: 
      self.threadWorkerCopy(inputList) 


    def CopyWorker(self, progressBar): 
     while True: 
      fileName = fileQueue.get() 
      if fileName is None: 
       fileQueue.task_done() 
       break 

      shutil.copy(fileName[0], fileName[1]) 
      fileQueue.task_done() 

      with self.lock: 
       self.copyCount += 1 
       if not progressBar: 
        percent = (self.copyCount * 100)/self.totalFiles 
        print "File copy: " + str(percent) + "%" 
       else: 
        progressBar.setValue(self.copyCount) 


    def threadWorkerCopy(self, fileNameList, progressBar=False): 
     threads = [] 
     threadCount = 10 

     for fileName in fileNameList: 
      fileQueue.put(fileName) 
     for i in range(threadCount): 
      t = threading.Thread(target=self.CopyWorker, args=(progressBar,)) 
      t.daemon = True 
      t.start() 
      threads.append(t) 
      fileQueue.put(None) 
     for t in threads: 
      t.join() 

답변

1

을 (AN exemple을 바라) t.start()list 모든 스레드를 추가해야합니까? CopyWorker에는 while True 루프에서 벗어나는 것이 없으므로 스레드가 무기한으로 살아있을 것으로 기대합니다. 모든 항목이 소비되면 빈 대기열의 다른 값인 get으로 영구적으로 차단되지만 해당 자원을 종료하거나 해제하지 않습니다.

더 이상 수행 할 작업이 없을 때 스레드를 종료하려면 스레드에 작업을 지시해야합니다. 이를 수행하는 일반적인 방법은 큐를 통해 센티널 값을 보내는 것인데, 소비 스레드는 데이터가 더 이상 없다는 신호로 인식합니다. 시작한 각 스레드에 대해 센티넬 사본 하나를 보내야합니다. 다음은 현재 코드를 기반으로 한 신속한 테스트되지 않은 솔루션입니다. 나는 그것이 파일 이름에 대한 정상적인 값일 수있는 것처럼 보이지 않기 때문에, 센티넬으로 None을 사용하고있다.

def CopyWorker(self, progressBar): 
    while True: 
     fileName = fileQueue.get() 
     if fileName is None:    # check for sentinel value here 
      fileQueue.task_done() 
      return 
     shutil.copy(fileName[0], fileName[1]) 
     fileQueue.task_done() 
     with self.lock: 
      self.copyCount += 1 
      if not progressBar: 
       print str(self.copyCount) + "of" + str(self.totalFiles) 
       percent = (self.copyCount * 100)/self.totalFiles 
       print "File copy: " + str(percent) + "%" 
      else: 
       progressBar.setValue(self.copyCount) 


def threadWorkerCopy(self, fileNameList, progressBar=False): 
    threadCount = 10 
    for i in range(threadCount): 
     t = threading.Thread(target=self.CopyWorker, args=(progressBar,)) 
     t.daemon = True 
     t.start() 
    for fileName in fileNameList: 
     fileQueue.put(fileName) 
    for i in range(threadCount):  # send sentinel values from here 
     fileQueue.put(None) 
    fileQueue.join() 

간략하게하기 위해 생략 할 수있는 몇 가지 다른 작업이 있습니다. 예를 들어 시작하는 각 스레드에 대한 참조를 유지하고 모두 join 주 스레드의 참조를 모두 종료하여 모두 종료했는지 확인하는 것이 좋습니다. 이는 아마도 대기열에 들어가는 대신에 join 일 수 있습니다. 또한 스레드가 제대로 종료되는 경우 스레드가 데몬이 될 이유가 없습니다.

두 개의 for i in range(threadCount) 루프가 필요하지 않도록 코드의 일부를 재정렬 할 수도 있습니다. 스레드를 시작하기 전에 먼저 put 모든 값을 대기열에 넣으면 두 루프를 결합 할 수 있습니다.

+0

감사합니다. 매우 도움이되었습니다. while 루프가 닫히지 않았을 때 같은 생각을 했었습니다. 그러나 문제가 아니라고 생각했습니다. 문서 (doc)에서 가져 왔기 때문입니다 (https://docs.python.org/2/library/queue.html).). 내 고정 코드로 위의 내 질문을 업데이 트했습니다. – Spencer

+0

링크 된 예제 코드는 대기중인 작업이 완료된 직후 종료되는 전체 스크립트로 잘 작동합니다.그러나 장기 실행 프로그램 내의 기능에 대해서는 매우 나쁜 접근 방식입니다. 그 설계는 쓰레드를 "누설"하지만 짧은 해석 스크립트에서는 인터프리터가 종료 될 때 정리되기 때문에 (쓰레드는 데몬으로 설정되어 인터프리터가 종료되는 것을 막지 못하기 때문에) 괜찮습니다. 고정 된 코드에서't.daemon = True' 라인과'task_done' 호출을 버릴 수 있습니다. 왜냐하면 여러분은 더 이상 큐에'join'하지 않기 때문입니다. – Blckknght

+0

나는 이것들을 데몬틱하게 만들 이유가 없으며이 시점에서 task_done은 단지 중복되어있다. 나는 어떤 이유로 든 여전히 필요하다고 생각했지만 queue.join 함수에서만 볼 수 있습니다. 감사! – Spencer

0

입니다. documentation

fileQueue.join() 뒤에 코드를 추가해야합니다. 그러나 당신은 당신이 스레드가 죽을 것이라고 생각되는 이유는 무엇

for i in range(threadCount): 
    fileQueue.put(None) 
for t in threads: 
    t.join() 
+0

감사합니다. AndMar하지만이 작업을 수행하지는 않습니다. 기본적으로 같은 작업을 수행하는 대기열에 참여했습니다 (대기열이 이동하기 전에 완료 될 때까지 대기). 즉, 내가 당신과 Blckknght에 동의합니다. 이것은 큐에 합류하는 것보다 더 좋은 아이디어입니다. 그래서 그것을 제 코드에 포함 시켰습니다! – Spencer

관련 문제