title: How to prevent crashing when running multi-threaded calculations then plotting the emitted results
body: <p>I am writing a PySide2 app that plots the results to a certain calculation and trying to multithread the calculation to avoid locking up the GUI. I am trying to use QThreadPool to, upon interacting with options relating to the plot, run the calculation in a separate thread that returns the results via a signal to a callback method, which plots the results using matplotlib.</p>
  
  <p>The problem is that when I change the selection of options in too quick (but not unreasonably quick) succession, the app crashes. This does not occur if the threading is removed.</p>
  
  <p>I know a lot of problems are caused by having the plotting happen in the worker thread rather than the main thread so I believe I have made sure that the plotting only happens in the main thread.</p>
  
  <p>I guess part of the problem is that I might be misunderstanding what is running where when using signals and slots. I have tried finding what thread is being used at different points in the code but can only use QThread.currentThread(),  which returns the address and doesn't really help as QThread.currentThreadId() leads to this error: AttributeError: type object 'PySide2.QtCore.QThread' has no attribute 'currentThreadId'.</p>
  
  <p>I have tried to isolate the behaviour by writing a minimal version of the app that crashes similarly, the majority of which I have put below. I have excluded the calculation as I am not sure if I can share it and I have replaced the plot options with a QListWidget with a few options. It requires more interaction to crash than the proper app that in some cases crashes after selecting just a few options in the space of a second or two but hopefully illustrates the point.</p>
  
  <pre><code>class MainWindow(QObject):
      def __init__(self, parent=None):
          super(MainWindow, self).__init__(parent)
          self.main_window = QMainWindow()
          self.main_window.setCentralWidget(QWidget())
          self.main_window.centralWidget().setLayout(QHBoxLayout())
          self.setup_main_window()
  
      def setup_main_window(self):
          print(f'setup_main_window thread address: {QThread.currentThreadId()}')
          self.load_list()
          self.plot_figure = PlotFigure()
          self.canvas = FigureCanvas(self.plot_figure)
          self.plot_figure.plot(update=False)
          self.main_window.centralWidget().layout().addWidget(self.canvas)
  
      def load_list(self):
          self.order_list = QListWidget(self.main_window)
          self.list_items = [
              QListWidgetItem('1', self.order_list),
              QListWidgetItem('2', self.order_list),
              QListWidgetItem('3', self.order_list),
              QListWidgetItem('4', self.order_list),
          ]
          self.order_list.itemClicked.connect(self.order_list_item_changed)
          self.main_window.centralWidget().layout().addWidget(self.order_list)
  
      def order_list_item_changed(self):
          print(f'order_list_item_changed thread address: {QThread.currentThreadId()}')
          self.plot_figure.plot()
  
      def show(self):
          if self.main_window is not None:
              self.main_window.show()
  
  class PlotFigure(Figure):
      def __init__(self):
          super().__init__()
  
      def plot(self):
          print(f'plot thread address: {QThread.currentThreadId()}')
          print(f'update: {update}')
          print(f'connecting signals')
          worker = Worker(self.calc)
          #worker.signals.close.connect(self.set_end_calc)
          worker.signals.finished.connect(self.plot_result)
          print(f'threads: {QThreadPool.globalInstance().activeThreadCount()}')
          QThreadPool.globalInstance().start(worker)
  
      def plot_result(self, m, xs, ys):
          print(f'plot_result thread address: {QThread.currentThreadId()}')
          print('plotting')
          fig = self.canvas.figure
          fig.clear()
          self.axis = fig.add_subplot(111)
          self.image = self.axis.imshow(m,
                  origin='lower', 
                  aspect='auto',
                  cmap=matplotlib.cm.get_cmap('inferno'), 
                  interpolation='bilinear',
                  extent=(xs[0], xs[-1], ys[0], ys[-1])
          )
          self.canvas.draw()
  </code></pre>
  
  <pre><code>class WorkerSignals(QtCore.QObject):
      close = QtCore.Signal(bool)
      start = QtCore.Signal(bool)
      finished = QtCore.Signal(list, list, list)
  
  class Worker(QtCore.QRunnable):
      def __init__(self, worker_method):
          super(Worker, self).__init__()
          self.signals = WorkerSignals()
          self.worker_method = worker_method
  
      def run(self):
          self.signals.close.emit(True)
          print('close signal sent')
          m, xs, ys = self.worker_method()
          print('calc done')
          self.signals.finished.emit(m, xs, ys)
  </code></pre>
  
  <p>I should be able to select new options (click around the list widget), start a new thread from the threadpool, which runs the calculation and sends back the results to be plotted. When too many options are selected in a short time, the app crashes. This doesn't happen when everything happens in the main thread.</p>
  
  <p>Can anyone tell me why the app might be crashing and offer solutions to fix the crash?</p>