Ich bin zur Zeit etwas skeptisch gegenüber meiner Multithreading Klassen, es fühlt sich im allgemeinen sehr Ressourcenlastig an und das "spawnen" von neuen Threads wird von Zeit zur Zeit auch immer langsamer. Vorgabe ist es Threads zu starten welche eine Liste von URL's abfragen (können Tausende sein) und den Status an die GUI übergeben, dieses sollte pausiert und auch gestoppt werden können. Die Summe an gleichzeitig laufenden Threads sollte einstellbar sein. Ich habe dazu eine vereinfachte GUI mit den notwendigen Thread Klassen erstellt.
Das ganze läuft unter Qt4 mit Python 2.
Ich bin für jeden Rat und Lösungsansatz dankbar!
Es tut mir im Voraus leid, dass ich so viel Code poste. Ich wüsste allerdings nicht, wie ich das ganze vereinfachen sollte so dass man dies auch testen kann.
GUI
Code: Alles auswählen
import sys
import requests
from PyQt4 import QtCore, QtGui
from random import choice
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_MainWindow(object):
def __init__(self):
self.thread_manager = None
self.url_list = None
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(410, 301)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.result_list = QtGui.QListWidget(self.centralwidget)
self.result_list.setGeometry(QtCore.QRect(10, 10, 391, 221))
self.result_list.setObjectName(_fromUtf8("result_list"))
self.widget = QtGui.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(10, 240, 391, 56))
self.widget.setObjectName(_fromUtf8("widget"))
self.verticalLayout = QtGui.QVBoxLayout(self.widget)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
self.start_button = QtGui.QPushButton(self.widget)
self.start_button.setObjectName(_fromUtf8("start_button"))
self.horizontalLayout.addWidget(self.start_button)
self.stop_button = QtGui.QPushButton(self.widget)
self.stop_button.setObjectName(_fromUtf8("stop_button"))
self.horizontalLayout.addWidget(self.stop_button)
self.verticalLayout.addLayout(self.horizontalLayout)
self.pause_button = QtGui.QPushButton(self.widget)
self.pause_button.setObjectName(_fromUtf8("pause_button"))
self.verticalLayout.addWidget(self.pause_button)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
self._connect_widgets()
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "Multithreading Tests", None))
self.start_button.setText(_translate("MainWindow", "Start", None))
self.stop_button.setText(_translate("MainWindow", "Stop", None))
self.pause_button.setText(_translate("MainWindow", "Pause", None))
def _connect_widgets(self):
self.start_button.clicked.connect(lambda: self._thread_handler(0))
self.pause_button.clicked.connect(lambda: self._thread_handler(1))
self.stop_button.clicked.connect(lambda: self._thread_handler(2))
# Liste mit vielen, vielen, vielen Url's
self.url_list = open('urlList.txt', 'rb').read().splitlines()
def _thread_handler(self, cmd):
if cmd == 0:
self.thread_manager = ThreadManager(self.url_list)
self.thread_manager.connect(self.thread_manager, QtCore.SIGNAL('WORKER_DONE'), self._add_result)
self.thread_manager.start()
elif cmd == 1:
self.thread_manager.pause()
elif cmd == 2:
self.thread_manager.stop()
def _add_result(self, status):
self.result_list.addItem(status)
def main():
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Lösung mit QThread's - Threads lassen sich abrupt terminieren. Allerdings ist die Erstellung einer Threadliste wahrscheinlich nicht die eleganteste Lösung.
Code: Alles auswählen
class ThreadManager(QtCore.QThread):
def __init__(self, url_list):
QtCore.QThread.__init__(self)
self.url_list = url_list
self.thread_pool = []
self.max_threads = 5
self.paused = False
self.stopped = False
def run(self):
while True:
if not self.paused:
if len(self.thread_pool) < self.max_threads:
thread = WorkerThread(choice(self.url_list))
thread.connect(thread, QtCore.SIGNAL('DONE'), self.return_result)
thread.start()
self.thread_pool.append(thread)
if self.stopped:
break
def return_result(self, result):
self.emit(QtCore.SIGNAL('WORKER_DONE'), result[0])
self.thread_pool.remove(result[1])
def stop(self):
self.stopped = False if self.stopped else True
# Terminate running QThreads
[t.terminate() for t in self.thread_pool]
def pause(self):
self.paused = False if self.stopped else True
class WorkerThread(QtCore.QThread):
def __init__(self, url):
QtCore.QThread.__init__(self)
self.url = url
def run(self):
try:
a = requests.get(self.url).status_code
self.emit(QtCore.SIGNAL('DONE'), [str(a), self])
except:
# Richtiger Weg um ein Thread mit den gleichen Konditionen neu zu starten?
self.run()
Code: Alles auswählen
class ThreadManager(QtCore.QThread):
def __init__(self, url_list):
QtCore.QThread.__init__(self)
self.url_list = url_list
self.thread_pool = QtCore.QThreadPool()
self.thread_pool.setMaxThreadCount(5)
self.queue = 0
self.paused = False
self.stopped = False
def run(self):
while True:
if not self.paused:
if self.queue <= 30: # Limitieren des ThreadPools
thread = WorkerThread(choice(self.url_list))
thread.signals.result.connect(self.return_result)
self.queue += 1
self.thread_pool.start(thread)
if self.stopped:
break
self.thread_pool.waitForDone()
def return_result(self, result):
self.queue -= 1
self.emit(QtCore.SIGNAL('WORKER_DONE'), result)
def stop(self):
self.stopped = False if self.stopped else True
def pause(self):
self.paused = False if self.stopped else True
class WorkerThread(QtCore.QRunnable):
def __init__(self, url):
QtCore.QRunnable.__init__(self)
self.url = url
self.signals = WorkerSignals()
def run(self):
try:
a = requests.get(self.url).status_code
self.signals.result.emit(str(a))
except:
# Richtiger Weg um ein Thread mit den gleichen Konditionen neu zu starten?
self.run()
class WorkerSignals(QtCore.QObject):
finished = QtCore.pyqtSignal()
error = QtCore.pyqtSignal(tuple)
result = QtCore.pyqtSignal(object)
progress = QtCore.pyqtSignal(int)