我有一个包含用户名/主机信息的对象字典,用于连接到服务器,并希望每个连接都在它自己的线程中运行,但无论我是否尝试,似乎最多只能从 100 个测试对象中获得 3 个正确连接使用concurrent.futures或threading模块。我知道这与当前线程在前一个线程完成之前执行有关,因为我设法通过在每个连接之间休眠 2 秒来将一个用户连接到 3 个用户,但这当然不是一个手动处理的可行解决方案,加上我注意到将睡眠时间增加到 5 秒或更多秒仍然只能连接 3 个用户,这让我想也许还有另一个问题?
我一直在玩弄 executor.shutdown 的 wait-keyword ,但没有真正理解它,但运气不好,在某处读到该问题可能与 GIL 有关,但根据其他来源,这不会影响我想做的事情,就我个人而言,除了运行多处理而不是线程之外,我根本不知道有什么方法可以解决这个问题。
number_of_objs = 100
def run_(obj):
obj.connect()
objs = {}
for i in range(number_of_objs):
nickname = f"{nickname_prefix}_{i}"
hostname = "server.example.com"
port = 1234
objs[i] = CreateObjs(nickname, hostname, port)
with concurrent.futures.ThreadPoolExecutor() as executor:
for k in objs.keys():
time.sleep(2)
executor.submit(run_, objs[k])
# Result:
# Testuser_0 successfully connected to server.example.com at port 1234
# Testuser_1 successfully connected to server.example.com at port 1234
# Testuser_2 successfully connected to server.example.com at port 1234
如果我猜对了你的意图,
ThreadPoolExecutor
在这里不是正确的结构。我们先来测试一下下面的非并发代码:
for k in objs.keys():
run_(objs[k])
此代码应按顺序建立连接,并且下一个连接应在前一个连接结束后开始。如果您想使用
ThreadPoolExecutor
,此代码应该可以工作。
ThreadPoolExecutor
实际上所做的是,它同时运行多个任务,如果任务数量大于worker数量,则其余任务会排队等待前面的任务结束。 GIL 的问题是,如果任务仅包含计算,则 ThreadPoolExecutor
无法按预期运行,因为由于 GIL,它们无法以真正的多线程方式运行。现在,如果 run_
中的任务开始等待远程服务器,开始发送测试日期或类似的内容,ThreadPoolExecutor
将不起作用,因为第一个任务永远不会结束。
如果希望启动 100 个并行线程来执行一些 I/O,则应该使用:
for k in objs.keys():
threading.Thread(target=run_, args=(objs[k],)).start()
注意:您不能像这样启动任意数量的线程。 100 就可以了,也可能是 1000,但是活动线程的数量有实际限制。