4.1 KiB
Python 线程
既然我们已经设置了基线,我们可以尝试通过使用 Python 线程库来提高代码的速度。您很快就会发现,当您的软件受 CPU 限制时,线程就没有多大用处了。这就是为什么我还将展示 IO 绑定软件如何从线程化中获得巨大好处。如果你需要一个关于 IO 绑定和 CPU 绑定之间的区别的提示,请首先阅读我关于 Python 并发性的文章。
目录
一个 CPU 绑定的 Python 线程示例
在下面的例子中,我们使用多线程来运行我们的重 函数,再次运行 80 次。每次调用都有自己的线程:
import threading
import time
# A CPU heavy calculation, just
# as an example. This can be
# anything you like
def heavy(n, myid):
for x in range(1, n):
for y in range(1, n):
x**y
print(myid, "is done")
def threaded(n):
threads = []
for i in range(n):
t = threading.Thread(target=heavy, args=(500,i,))
threads.append(t)
t.start()
for t in threads:
t.join()
if __name__ == "__main__":
start = time.time()
threaded(80)
end = time.time()
print("Took: ", end - start)
这个线程版本在我的系统上运行大约需要 47 秒。如果繁重的函数有很多阻塞 IO,比如网络调用或文件系统操作,这个版本将是一个很大的优化。原因是而不是CPU 绑定函数的优化是 Python GIL !
如果 Python 没有 GIL,速度会快得多。然而,尽管有 80 个线程,它的运行速度和基线差不多。实际上,基线甚至更快,因为它没有线程创建和线程间切换的所有开销。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
一个 IO 绑定的 Python 线程示例
这是工作中的 GIL。每个线程轮流运行,而不是同时运行。为了证明这一点,如果 heavy 是一个 I/O 绑定的函数,这将会给我们带来巨大的速度提升,我们将创建一个小测试!我们可以使用time.sleep()来模拟 I/O 绑定软件。睡眠与阻塞 IO 有着相同的效果:它允许 CPU 做其他事情,并在特定时间过后返回。
我在下一段代码中修改了 heavy 函数:
import threading
import time
# An I/O intensive calculation.
# We simulate it with sleep.
def heavy(n, myid):
time.sleep(2)
print(myid, "is done")
def threaded(n):
threads = []
for i in range(n):
t = threading.Thread(target=heavy, args=(500,i,))
threads.append(t)
t.start()
for t in threads:
t.join()
if __name__ == "__main__":
start = time.time()
threaded(80)
end = time.time()
print("Took: ", end - start)
即使我们有 80 个 Python 线程都休眠了两秒钟,这段代码仍然在两秒多一点的时间内完成。当休眠时,Python 线程库可以调度其他线程运行。太棒了。
继续学习
如果你想了解更多关于 Python 线程的知识,请务必阅读官方文档。您可能很好奇我们是否以及如何优化 CPU 受限的代码。这正是我们将在下一篇关于 Python 多处理的文章中发现的。