lst = [1, 2, 3, 4, 5, 6, 7, 8, 9] class Thread(QRunnable): def __init__(self): super(Thread, self).__init__() self.mutex = QMutex() def run(self): self.mutex.lock() lst.pop(0) print(str(lst)) time.sleep(5) self.mutex.unlock()
The code above is an example of what I am trying to acheive, I have a list that is defined outside the class. I want to periodically pop the first element of the list. If I am running 5 threads I only want one thread to mutate the list at single time. Everytime I try this the 5 threads all try to pop the first element and dont wait as I want. When I recreate this in the native python threading library, it works as intended. What am I doing wrong here?
The problem is that you create a mutex per thread. A mutex lock only protects from threads using the same mutex. Since each thread is using its own private lock, its only protected from itself. Expanding on @eyllanesc answer, I’ve created a single mutex for all threads to use. The mutex should be considered associated with the data it protects.
import sys import time from PyQt6.QtCore import QCoreApplication, QMutex, QRunnable, QThreadPool, QTimer # data with mutex access controls mutex = QMutex() lst = [1, 2, 3, 4, 5, 6, 7, 8, 9] class Thread(QRunnable): def run(self): #mutex = QMutex() <-- don't create a private mutex mutex.lock() lst.pop(0) print(lst, time.time()-start, flush=True) time.sleep(5) mutex.unlock() start = time.time() def main(): app = QCoreApplication(sys.argv) QTimer.singleShot(8 * 1000, QCoreApplication.quit) pool = QThreadPool() for i in range(5): runnable = Thread() pool.start(runnable) sys.exit(app.exec()) if __name__ == "__main__": main()