docs: def create_tasks(task_coro, addrs, *args, flatten=True, **kwargs):
      '''Create and schedule a set of asynchronous tasks.
  
      The function creates the tasks using a given list of agent addresses and
      wraps each of them in :func:`asyncio.ensure_future`. The ``*args`` and
      ``**kwargs`` are passed down to :func:`task_coro` when creating tasks for
      each address in :attr:`addrs`.
  
      Usage example for a method in a class derived from
      :class:`~creamas.mp.MultiEnvironment`::
  
          async def my_method(self, *args, **kwargs):
              async def task(addr, *args, **kwargs):
                  r_manager = await self.env.connect(addr)
                  return await r_manager.my_method(*args, **kwargs)
  
              return await util.create_tasks(task, self.addrs, *args, **kwargs)
  
      :param task_coro:
          Coroutine which is used for each address in :attr:`addrs`. The
          coroutine should accept an agent address as the first parameter.
      :param list addrs:
          A list of agent addresses used as the first parameters of
          :func:`task_coro`.
      :param bool flatten:
          If ``True`` the returned results are flattened into one list if the
          tasks return iterable objects. The parameter does nothing if all the
          results are not iterable.
      :returns:
          An awaitable coroutine which returns the results of tasks as a list or
          as a flattened list
      '''
      tasks = []
      for agent_addr in addrs:
          task = asyncio.ensure_future(task_coro(agent_addr, *args, **kwargs))
          tasks.append(task)
      return wait_tasks(tasks, flatten)