This is a parallel version of the traceroute primitive.

    :param input_list: the input is a list of domain names
    :param method: the packet type used for traceroute, UDP by default
    :param cmd_arguments: the list of arguments that need to be passed
                        to traceroute.
    :param delay_time: delay before starting each thread
    :param max_threads: maximum number of concurrent threads
    :return:

def traceroute_batch(input_list, results={}, method="udp", cmd_arguments=None,
                     delay_time=0.1, max_threads=100):
    """
    This is a parallel version of the traceroute primitive.

    :param input_list: the input is a list of domain names
    :param method: the packet type used for traceroute, UDP by default
    :param cmd_arguments: the list of arguments that need to be passed
                        to traceroute.
    :param delay_time: delay before starting each thread
    :param max_threads: maximum number of concurrent threads
    :return:
    """
    threads = []
    thread_error = False
    thread_wait_timeout = 200
    ind = 1
    total_item_count = len(input_list)
    for domain in input_list:
        wait_time = 0
        while threading.active_count() > max_threads:
            time.sleep(1)
            wait_time += 1
            if wait_time > thread_wait_timeout:
                thread_error = True
                break

        if thread_error:
            results["error"] = "Threads took too long to finish."
            break

        # add just a little bit of delay before starting the thread
        # to avoid overwhelming the connection.
        time.sleep(delay_time)

        log_prefix = "%d/%d: " % (ind, total_item_count)
        thread = threading.Thread(target=traceroute,
                                  args=(domain, method, cmd_arguments,
                                        results, log_prefix))
        ind += 1
        thread.setDaemon(1)
        thread_open_success = False
        retries = 0
        while not thread_open_success and retries < MAX_THREAD_START_RETRY:
            try:
                thread.start()
                threads.append(thread)
                thread_open_success = True
            except:
                retries += 1
                time.sleep(THREAD_START_DELAY)
                logging.error("%sThread start failed for %s, retrying... (%d/%d)" % (log_prefix, domain, retries, MAX_THREAD_START_RETRY))

        if retries == MAX_THREAD_START_RETRY:
            logging.error("%sCan't start a new thread for %s after %d retries." % (log_prefix, domain, retries))

    for thread in threads:
        thread.join(thread_wait_timeout)

    return results