我遇到了互斥问题,无法弄清楚为什么锁和解锁之间的代码在所有线程中同时运行。 这是我的线程类:
class MyThread(QtCore.QThread):
mutex = QtCore.QMutex()
load_message_input = QtCore.pyqtSignal()
def __init__(self, id, window):
super(MyThread, self).__init__()
self.id = id
self.window = window
def run(self):
print "Thread %d is started" % self.id
self.get_captcha_value()
print "Thread %d is finishing" % self.id
def get_captcha_value(self):
MyThread.mutex.lock()
print "Thread %d locks mutex" % self.id
self.load_message_input.connect(self.window.show_input)
self.load_message_input.emit()
self.window.got_message.connect(self.print_message)
self.window.input_finished.wait(self.mutex)
print "Thread %d unlocks mutex" % self.id
MyThread.mutex.unlock()
@QtCore.pyqtSlot("QString")
def print_message(self, msg):
print "Thread %d: %s" % (self.id, msg)
以下是我描述窗口的方法:
class MyDialog(QtGui.QDialog):
got_message = QtCore.pyqtSignal("QString")
def __init__(self, *args, **kwargs):
super(MyDialog, self).__init__(*args, **kwargs)
self.last_message = None
self.setModal(True)
self.message_label = QtGui.QLabel(u"Message")
self.message_input = QtGui.QLineEdit()
self.dialog_buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
self.dialog_buttons.accepted.connect(self.accept)
self.dialog_buttons.rejected.connect(self.reject)
self.hbox = QtGui.QHBoxLayout()
self.hbox.addWidget(self.message_label)
self.hbox.addWidget(self.message_input)
self.vbox = QtGui.QVBoxLayout()
self.vbox.addLayout(self.hbox)
self.vbox.addWidget(self.dialog_buttons)
self.setLayout(self.vbox)
self.input_finished = QtCore.QWaitCondition()
@QtCore.pyqtSlot()
def show_input(self):
print "showing input"
self.show()
self.setModal(True)
@QtCore.pyqtSlot()
def on_accepted(self):
print "emit: ", self.message_input.text()
self.got_message.emit(self.message_input.text())
self.input_finished.wakeAll()
这是主线:
import sys
app = QtGui.QApplication(sys.argv)
window = test_qdialog.MyDialog()
threads = []
for i in range(5):
thread = MyThread(i, window)
if not thread.isRunning():
thread.start()
threads.append(thread)
sys.exit(app.exec_())
输出如下:
Thread 0 is startedThread 1 is startedThread 4 is started
Thread 0 locks mutexThread 3 is started
Thread 2 is started
Thread 2 locks mutex
Thread 3 locks mutex
Thread 1 locks mutex
Thread 4 locks mutex
showing input
showing input
showing input
showing input
showing input
更新 感谢Yoann的建议。 以下是MyThread类代码的现状:
class MyThread(QtCore.QThread):
mutex = QtCore.QMutex()
load_message_input = QtCore.pyqtSignal()
def __init__(self, id, window):
super(MyThread, self).__init__()
self.id = id
self.window = window
# self.mutex = QtCore.QMutex()
self.load_message_input.connect(self.window.show_input)
def run(self):
print "Thread %d is started" % self.id
self.get_captcha_value()
print "Thread %d is finishing" % self.id
def get_captcha_value(self):
MyThread.mutex.lock()
print "Thread %d locks mutex" % self.id
self.load_message_input.emit()
mutex2 = QtCore.QMutex()
mutex2.lock()
self.window.got_message.connect(self.print_message)
self.window.input_finished.wait(mutex2)
mutex2.unlock()
self.window.got_message.disconnect(self.print_message)
print "Thread %d unlocks mutex" % self.id
MyThread.mutex.unlock()
@QtCore.pyqtSlot("QString")
def print_message(self, msg):
print "Thread %d: %s" % (self.id, msg)
现在我在第一个线程完成后得到了这个异常:
Traceback (most recent call last):
File "/path/to/script/qdialog_threads.py", line 20, in run
self.get_captcha_value()
File "path/to/script/qdialog_threads.py", line 34, in get_captcha_value
MyThread.mutex.unlock()
AttributeError: 'NoneType' object has no attribute 'mutex'
答案 0 :(得分:0)
您没有在print
来电中刷新缓冲区。另请注意,showing input
输出是在GUI线程中生成的,因此可能会在互斥锁被锁定后的任何时间发生 - 包括互斥锁已经解锁后。
答案 1 :(得分:0)
您的代码中存在多个问题
您的打印电话已全部混淆
可以通过以这种方式锁定每个打印来轻松修复(with
语句是处理获取和释放锁的上下文管理器):
# Lock declaration
CONSOLELOCK = threading.Lock()
# Then for any print
with CONSOLELOCK:
print("Anything")
正如Kuba Ober所说,显示输入输出是在GUI线程中生成的,所以无论如何它可能会出现错误的顺序。
当您应该使用两个
时,使用一个互斥锁当您调用self.window.input_finished.wait(self.mutex)
时,互斥锁被释放,因此每个其他线程都会尝试获取它。第一个到达将获取它,发出load_message
然后等待input_finished并释放互斥锁,依此类推。最终,每个线程都会锁定互斥锁,发出load_message
并等待相同的条件(同一对话框的接受信号)。一种解决方案是使用第二个互斥锁,而不是根据您的情况使用。 self.window.input_finished.wait(self.mutex2)
。
此外,您的on_accepted
广告位从未被调用,您永远不会断开window.got_message
信号,以便线程保持活跃状态。