class_docstr: A thread-safe data structure used to respond to resource request,
     maintaining the available computation resources in each worker.
  
  Attributes:
      worker_hostname (dict): A dict to record worker hostname.
      worker_vacant_jobs (dict): Record which vacant jobs does each
                                 worker has.
      worker_used_jobs (dict): Record which used jobs does each
                                 worker has.
      master_ip (str): IP address of the master node.
class_docstr_tok: ["A","thread-safe","data","structure","used","to","respond","to","resource","request",",","maintaining","the","available","computation","resources","in","each","worker",".","Attributes",":","worker_hostname","(","dict",")",":","A","dict","to","record","worker","hostname",".","worker_vacant_jobs","(","dict",")",":","Record","which","vacant","jobs","does","each","worker","has",".","worker_used_jobs","(","dict",")",":","Record","which","used","jobs","does","each","worker","has",".","master_ip","(","str",")",":","IP","address","of","the","master","node","."]
docstr: When worker kill an old job, it will start a new job.
  
  Args:
      killed_job_address (str): The job address of the killed job.
      new_job(``InitializedJob``): Information of the new job.
      worker_address (str): The worker which kills an old job.
docstr_tok: ["When","worker","kill","an","old","job",",","it","will","start","a","new","job",".","Args",":","killed_job_address","(","str",")",":","The","job","address","of","the","killed","job",".","new_job","(","``","InitializedJob","``",")",":","Information","of","the","new","job",".","worker_address","(","str",")",":","The","worker","which","kills","an","old","job","."]
code: def update_job(self, killed_job_address, new_job, worker_address):
      """When worker kill an old job, it will start a new job.
  
      Args:
          killed_job_address (str): The job address of the killed job.
          new_job(``InitializedJob``): Information of the new job.
          worker_address (str): The worker which kills an old job.
      """
      with self.lock:
          self.worker_vacant_jobs[worker_address][
              new_job.job_address
          ] = new_job
  
          killed_job = None
          for vacant_jobs in self.worker_vacant_jobs.values():
              if killed_job_address in vacant_jobs:
                  killed_job = vacant_jobs.pop(killed_job_address)
                  break
          for used_jobs in self.worker_used_jobs.values():
              if killed_job_address in used_jobs:
                  killed_job = used_jobs.pop(killed_job_address)
                  break
  
          self.remove_job(killed_job)
  
imports: import threading
  import random
  from collections import defaultdict
  from collections import namedtuple
  from parl.utils import logger
  from parl.remote import remote_constants
  from parl.remote.cpu_resource import CpuResource
  from parl.remote.gpu_resource import GpuResource
  from parl.remote.message import AllocatedCpu
  from parl.remote.message import AllocatedGpu
label_desc: Use image node_id 12 for calling the WorkerManager obj's underlying member method code with example usage: obj.update_job(killed_job_address, new_job, worker_address) without return types