code: def send(
      self,
      channel_name,
      channel_type,
      channel_value,
      channel_namespace=ChannelNamespace.USER,
  ):
      # Before taking the lock here, we need to check if the sending thread is not running yet.
      # Otherwise, the sending thread could call send() while being join()-ed, which would result
      # in a deadlock.
      if not self._is_running():
          with self.__LOCK:
              if not self._is_running():
                  self._start()
  
      if channel_namespace == ChannelNamespace.USER:
          namespaced_channel_map = self._user_channel_name_to_id_map
      else:
          namespaced_channel_map = self._system_channel_name_to_id_map
  
      if channel_name in namespaced_channel_map:
          channel_id = namespaced_channel_map[channel_name]
      else:
          response = self._experiment._create_channel(
              channel_name, channel_type, channel_namespace
          )
          channel_id = response.id
          namespaced_channel_map[channel_name] = channel_id
  
      self._values_queue.put(
          self._QUEUED_CHANNEL_VALUE(
              channel_id=channel_id,
              channel_name=channel_name,
              channel_type=channel_type,
              channel_value=channel_value,
              channel_namespace=channel_namespace,
          )
      )
  
imports: import logging
  import threading
  import time
  from collections import namedtuple
  from itertools import groupby
  from queue import Empty, Queue
  from bravado.exception import HTTPUnprocessableEntity
  from neptune.legacy.exceptions import NeptuneException
  from neptune.legacy.internal.channels.channels import ChannelIdWithValues, ChannelNamespace, ChannelType, ChannelValue
  from neptune.legacy.internal.threads.neptune_thread import NeptuneThread
label_desc: Use image node_id 2 for calling the ChannelsValuesSender obj's underlying member method code with example usage: obj.send(channel_name, channel_type, channel_value, channel_namespace) without return types