class_docstr: The dispatcher is responsible for assigning tasks to free workers, report results back to the master and
  communicate to the nameserver.
code: def register_result(self, id=None, result=None):
      self.logger.debug("DISPATCHER: job %s finished" % (str(id)))
      with self.runner_cond:
          self.logger.debug(
              "DISPATCHER: register_result: lock acquired"
          )
          # fill in missing information
          job = self.running_jobs[id]
          job.time_it("finished")
          job.result = result["result"]
          job.exception = result["exception"]
  
          self.logger.debug(
              "DISPATCHER: job %s on %s finished"
              % (str(job.id), job.worker_name)
          )
          self.logger.debug(str(job))
  
          # delete job
          del self.running_jobs[id]
  
          # label worker as idle again
          try:
              self.worker_pool[job.worker_name].runs_job = None
              self.worker_pool[job.worker_name].proxy._pyroRelease()
              self.idle_workers.add(job.worker_name)
              # notify the job_runner to check for more jobs to run
              self.runner_cond.notify()
          except KeyError:
              # happens for crashed workers, but we can just continue
              pass
          except:
              raise
  
      # call users callback function to register the result
      # needs to be with the condition released, as the master can call
      # submit_job quickly enough to cause a dead-lock
      self.new_result_callback(job)
  
imports: import threading
  import logging
  import queue
  import time
  import Pyro4
label_desc: Use image node_id 10 for calling the Dispatcher obj's underlying member method code with example usage: obj.register_result(id, result) without return types