池仅执行一个线程,而不是4。如何使它成为无限线程?

时间:2019-02-14 20:49:17

标签: python multithreading python-2.7 concurrency python-multithreading

所以我正在研究一个小的Python工具来对应用程序的API进行压力测试。

我有一个使用Threading的很好的脚本,但是随后我读到它需要手动编码来维护n个并发线程(意味着,在旧线程完成后立即开始新线程),并在这里提出建议: How to start a new thread when old one finishes?是要使用ThreadPool,我尝试如下:

def test_post():
    print "Executing in " + threading.currentThread().getName() + "\n"
    time.sleep(randint(1, 3))
    return randint(1, 5), "Message"


if args.send:
    code, content = post()
    print (code, "\n")
    print (content)
elif args.test:
    # Create new threads
    print threads
    results_list = []
    pool = ThreadPool(processes=threads)
    results = pool.apply_async(test_post())
    pool.close()  # Done adding tasks.
    pool.join()  # Wait for all tasks to complete.
    # results = list(pool.imap_unordered(
    #     test_post(), ()
    # ))
    # thread_list = []
    # while threading.activeCount() <= threads:
    #     thread = LoadTesting(threadID=free_threads, name="Thread-" + str(threading.activeCount()), counter=1)
    #     thread.start()
    #     thread_list.append(thread)
    print "Exiting Main Thread" + "\n"
else:
    print ("cant get here!")

调用脚本时,得到一致的输出,例如:

  

4

     

在MainThread中执行

     

退出主线程

我不确定为什么。正如您在注释栏中所看到的,我尝试了不同的方法,但它仍然只执行一次。

我的目标是使脚本循环运行,始终随时运行n个线程。 test_post(和分别为post)函数返回HTTP响应代码,以及内容-我想稍后在响应代码不是200 OK时使用它来打印/停止。 / p>

1 个答案:

答案 0 :(得分:1)

您的第一个问题是您已经在MainThread中调用了以下函数:

pool.apply_async(test_post())

......而不是传递test_post作为要在工作线程中执行以下调用的参数:

pool.apply_async(test_post)

  

OP:我有一个使用Threading的漂亮脚本,但是我读到它需要手动编码来维护n个并发线程(这意味着,一旦旧线程完成,就开始新线程)... < / p>

您需要区分工作单元(作业,任务)和线程。首先使用池的全部目的是重新使用执行程序,无论是线程还是进程。实例化Pool时已经创建了工作程序,只要您不关闭Pool,所有初始线程都将保持活动状态。因此,您不必在意重新创建线程,只需在需要分配某些工作时就调用现有池的池方法。池将执行此作业(池方法调用)并从中创建任务。这些任务放在无限制的队列中。每当工人完成一项任务时,它将阻塞性地尝试get()inqueue来完成一项新任务。


  

OP:Pool只执行一个线程而不是4个线程...我尝试了不同的方法,但仍然只执行一次。

pool.apply_async(func, args=(), kwds={}, callback=None, error_callback=None)

...是一个单次调用,单个任务生成作业。如果要执行多个func,则必须多次调用pool.apply_async(),或者使用类似

的映射池方法
pool.map(func, iterable, chunksize=None)

...,它将一个函数映射到一个可迭代的对象上。 pool.apply_async是非阻塞的,这就是为什么它是“异步”的。它立即返回一个AsyncResult对象,您可以(阻塞地)调用.wait().get()


  

通过注释变得很明显,您希望无尽立即替换完成的任务(自行生成的输入流)...并且程序应停止在KeyboardInterrupt或结果没有特定值时。

您可以使用callback的{​​{1}}参数在所有旧任务完成后立即安排新任务。困难在于如何同时使用MainThread来防止整个脚本过早结束,同时保持其对KeyboardInterrupt的响应。让MainThread处于循环睡眠状态,使其仍然可以立即对KeyboardInterrupt做出反应,同时防止提前退出。如果结果应停止该程序,则可以让回调终止该池。然后MainThread只需在其睡眠循环中包括对池状态的检查即可。

apply_async

带有KeyboardInterrupt的示例输出:

import time
from random import randint, choice
from itertools import count
from datetime import datetime
from threading import current_thread
from multiprocessing.pool import ThreadPool


def test_post(post_id):
    time.sleep(randint(1, 3))
    status_code = choice([200] * 9 + [404])
    return "{} {} Message no.{}: {}".format(
        datetime.now(), current_thread().name, post_id, status_code
    ), status_code


def handle_result(result):
    msg, code = result
    print(msg)
    if code != 200:
        print("terminating")
        pool.terminate()
    else:
        pool.apply_async(
            test_post, args=(next(post_cnt),), callback=handle_result
        )


if __name__ == '__main__':

    N_WORKERS = 4

    post_cnt = count()

    pool = ThreadPool(N_WORKERS)

    # initial distribution
    for _ in range(N_WORKERS):
        pool.apply_async(
            test_post, args=(next(post_cnt),), callback=handle_result
        )

    try:
        while pool._state == 0:  # check if pool is still alive
            time.sleep(1)
    except KeyboardInterrupt:
        print(" got interrupt")

示例输出,由于不想要的返回值而终止:

$> python2 scratch.py
2019-02-15 18:46:11.724203 Thread-4 Message no.3: 200
2019-02-15 18:46:12.724713 Thread-2 Message no.1: 200
2019-02-15 18:46:13.726107 Thread-1 Message no.0: 200
2019-02-15 18:46:13.726292 Thread-3 Message no.2: 200
2019-02-15 18:46:14.724537 Thread-4 Message no.4: 200
2019-02-15 18:46:14.726881 Thread-2 Message no.5: 200
2019-02-15 18:46:14.727071 Thread-1 Message no.6: 200
^C got interrupt

请注意,在您的情况下,您的{@ {1}}调用次数也比$> python2 scratch.py 2019-02-15 18:44:19.966387 Thread-3 Message no.0: 200 2019-02-15 18:44:19.966491 Thread-4 Message no.1: 200 2019-02-15 18:44:19.966582 Thread-1 Message no.3: 200 2019-02-15 18:44:20.967555 Thread-2 Message no.2: 200 2019-02-15 18:44:20.968562 Thread-3 Message no.4: 404 terminating 次要多,以使初始分配具有一定的缓冲区来减少延迟。