class_docstr: Creates a multiprocess vectorized wrapper for multiple environments, distributing each environment to its own
  process, allowing significant speed up when the environment is computationally complex.
  
  For performance reasons, if your environment is not IO bound, the number of environments should not exceed the
  number of logical cores on your CPU.
  
  .. warning::
  
      Only 'forkserver' and 'spawn' start methods are thread-safe,
      which is important when TensorFlow sessions or other non thread-safe
      libraries are used in the parent (see issue #217). However, compared to
      'fork' they incur a small start-up cost and have restrictions on
      global variables. With those methods, users must wrap the code in an
      ``if __name__ == "__main__":`` block.
      For more information, see the multiprocessing documentation.
  
  :param env_fns: ([callable]) A list of functions that will create the environments
      (each callable returns a `Gym.Env` instance when called).
  :param start_method: (str) method used to start the subprocesses.
         Must be one of the methods returned by multiprocessing.get_all_start_methods().
         Defaults to 'forkserver' on available platforms, and 'spawn' otherwise.
class_docstr_tok: ["Creates","a","multiprocess","vectorized","wrapper","for","multiple","environments",",","distributing","each","environment","to","its","own","process",",","allowing","significant","speed","up","when","the","environment","is","computationally","complex",".","For","performance","reasons",",","if","your","environment","is","not","IO","bound",",","the","number","of","environments","should","not","exceed","the","number","of","logical","cores","on","your","CPU",".","..","warning",":",":","Only","'forkserver","'","and","'spawn","'","start","methods","are","thread-safe",",","which","is","important","when","TensorFlow","sessions","or","other","non","thread-safe","libraries","are","used","in","the","parent","(","see","issue","#","217",")",".","However",",","compared","to","'fork","'","they","incur","a","small","start-up","cost","and","have","restrictions","on","global","variables",".","With","those","methods",",","users","must","wrap","the","code","in","an","``","if","__name__","==","``","__main__","''",":","``","block",".","For","more","information",",","see","the","multiprocessing","documentation",".",":","param","env_fns",":","(","[","callable","]",")","A","list","of","functions","that","will","create","the","environments","(","each","callable","returns","a","`","Gym.Env","`","instance","when","called",")",".",":","param","start_method",":","(","str",")","method","used","to","start","the","subprocesses",".","Must","be","one","of","the","methods","returned","by","multiprocessing.get_all_start_methods","(",")",".","Defaults","to","'forkserver","'","on","available","platforms",",","and","'spawn","'","otherwise","."]
code: def __init__(self, env_fns, start_method=None):
      self.waiting = False
      self.closed = False
      n_envs = len(env_fns)
  
      # In some cases (like on GitHub workflow machine when running tests),
      # "forkserver" method results in an "connection error" (probably due to mpi)
      # We allow to bypass the default start method if an environment variable
      # is specified by the user
      if start_method is None:
          start_method = os.environ.get("DEFAULT_START_METHOD")
  
      # No DEFAULT_START_METHOD was specified, start_method may still be None
      if start_method is None:
          # Fork is not a thread safe method (see issue #217)
          # but is more user friendly (does not require to wrap the code in
          # a `if __name__ == "__main__":`)
          forkserver_available = (
              "forkserver" in multiprocessing.get_all_start_methods()
          )
          start_method = (
              "forkserver" if forkserver_available else "spawn"
          )
      ctx = multiprocessing.get_context(start_method)
  
      self.remotes, self.work_remotes = zip(
          *[ctx.Pipe(duplex=True) for _ in range(n_envs)]
      )
      self.processes = []
      for work_remote, remote, env_fn in zip(
          self.work_remotes, self.remotes, env_fns
      ):
          args = (work_remote, remote, CloudpickleWrapper(env_fn))
          # daemon=True: if the main process crashes, we should not cause things to hang
          process = ctx.Process(
              target=_worker, args=args, daemon=True
          )  # pytype:disable=attribute-error
          process.start()
          self.processes.append(process)
          work_remote.close()
  
      self.remotes[0].send(("get_spaces", None))
      observation_space, action_space = self.remotes[0].recv()
      VecEnv.__init__(
          self, len(env_fns), observation_space, action_space
      )
  
imports: import os
  import multiprocessing
  from collections import OrderedDict
  from typing import Sequence
  import gym
  import numpy
  from stable_baselines.common.vec_env.base_vec_env import VecEnv, CloudpickleWrapper
label_desc: Use image node_id 1 to create a new SubprocVecEnv object from inherited base classes: VecEnv with example: obj = SubprocVecEnv(env_fns, start_method)