class_docstr: Distributed Centralized Lock
  
  Parameters
  ----------
  names
      Names of the locks to acquire. Choosing the same name allows two
      disconnected processes to coordinate a lock.
  client
      Client to use for communication with the scheduler.  If not given, the
      default global client will be used.
  
  Examples
  --------
  >>> lock = MultiLock(['x', 'y'])  # doctest: +SKIP
  >>> lock.acquire(timeout=1)  # doctest: +SKIP
  >>> # do things with protected resource 'x' and 'y'
  >>> lock.release()  # doctest: +SKIP
code: def __init__(
      self, names: list[str] | None = None, client: Client | None = None
  ):
      try:
          self.client = client or Client.current()
      except ValueError:
          # Initialise new client
          self.client = get_worker().client
  
      self.names = names or []
      self.id = uuid.uuid4().hex
      self._locked = False
  
imports: from __future__ import annotations
  import asyncio
  import logging
  import uuid
  from collections import defaultdict
  from collections.abc import Hashable
  from dask.utils import parse_timedelta
  from distributed.client import Client
  from distributed.utils import TimeoutError, log_errors, wait_for
  from distributed.worker import get_worker
label_desc: Use image node_id 1 to create a new MultiLock object  with example: obj = MultiLock(names, client)