docstr: Starts worker threads.
  
  :param worker_class: A class of the worker class. The class will be instantiated in the worker process. The
    class must implement :class:`.WorkerBase` protocol
  :param worker_setup_args: Argument that will be passed to ``args`` property of the instantiated
    :class:`.WorkerBase`
  :return: ``None``
docstr_tok: ["Starts","worker","threads",".",":","param","worker_class",":","A","class","of","the","worker","class",".","The","class","will","be","instantiated","in","the","worker","process",".","The","class","must","implement",":","class",":","`",".WorkerBase","`","protocol",":","param","worker_setup_args",":","Argument","that","will","be","passed","to","``","args","``","property","of","the","instantiated",":","class",":","`",".WorkerBase","`",":","return",":","``","None","``"]
code: def start(self, worker_class, worker_args=None, ventilator=None):
      """Starts worker threads.
  
      :param worker_class: A class of the worker class. The class will be instantiated in the worker process. The
        class must implement :class:`.WorkerBase` protocol
      :param worker_setup_args: Argument that will be passed to ``args`` property of the instantiated
        :class:`.WorkerBase`
      :return: ``None``
      """
      # Verify stop_event and raise exception if it's already set!
      if self._stop_event.is_set():
          raise RuntimeError(
              "ThreadPool({}) cannot be reused! stop_event set? {}".format(
                  len(self._workers), self._stop_event.is_set()
              )
          )
  
      # Set up a channel to send work
      self._ventilator_queue = queue.Queue()
      self._results_queue = queue.Queue(self._results_queue_size)
      self._workers = []
      for worker_id in range(self.workers_count):
          worker_impl = worker_class(
              worker_id, self._stop_aware_put, worker_args
          )
          new_thread = WorkerThread(
              worker_impl,
              self._stop_event,
              self._ventilator_queue,
              self._results_queue,
              self._profiling_enabled,
          )
          # Make the thread daemonic. Since it only reads it's ok to abort while running - no resource corruption
          # will occur.
          new_thread.daemon = True
          self._workers.append(new_thread)
  
      # Spin up all worker threads
      for w in self._workers:
          w.start()
  
      if ventilator:
          self._ventilator = ventilator
          self._ventilator.start()
  
imports: import cProfile
  import pstats
  import random
  import sys
  from threading import Thread, Event
  from traceback import format_exc
  from six.moves import queue
  from petastorm.workers_pool import EmptyResultError, VentilatedItemProcessedMessage
label_desc: Use image node_id 2 for calling the ThreadPool obj's underlying member method code with example usage: obj.start(worker_class, worker_args, ventilator) without return types