【问题标题】:Some doubts about Thread Pool Executor and Thread in pythonpython中关于Thread Pool Executor和Thread的一些疑惑
【发布时间】:2020-06-04 05:50:01
【问题描述】:

最近尝试使用asyncio异步执行多个阻塞操作。我用了函数loop.run_in_executor,好像该函数将任务放入线程池。据我所知线程池,它减少了开销创建和销毁线程,因为它可以在任务完成时放入一个新任务而不是销毁线程。我编写了以下代码以获得更深层次的理解。

def blocking_funa():
    print('starta')
    print('starta')
    time.sleep(4)
    print('enda')
def blocking_funb():
    print('startb')
    print('startb')
    time.sleep(4)
    print('endb')
loop = asyncio.get_event_loop()
tasks = [loop.run_in_executor(None, blocking_funa), loop.run_in_executor(None, blocking_funb)]
loop.run_until_complete(asyncio.wait(tasks))

和输出:

starta
startbstarta

startb
(wait for about 4s)
enda
endb

我们可以看到这两个任务几乎是同时进行的。现在我使用线程模块:

threads = [threading.Thread(target = blocking_ioa), threading.Thread(target = blocking_iob)]
for thread in threads:
    thread.start()
    thread.join()

和输出:

starta
starta
enda
startb
startb
endb

由于GIL的限制,只有一个线程在同时执行,所以我理解输出。但是线程池执行器是如何使这两个任务几乎同时执行的。线程池和线程有什么区别?以及为什么线程池看起来好像不受 GIL 限制?

【问题讨论】:

  • Python 经常在 GIL 上发布/获取。这意味着可运行的 GIL 控制线程都将获得很少的 sprint。它不是平行的,只是交错的。

标签: python multithreading threadpool


【解决方案1】:

您没有进行公平的比较,因为您在开始第二个线程之前加入了第一个线程。

相反,请考虑:

import time
import threading


def blocking_funa():
    print('a 1')
    time.sleep(1)
    print('a 2')
    time.sleep(1)
    print('enda (quick)')


def blocking_funb():
    print('b 1')
    time.sleep(1)
    print('b 2')
    time.sleep(4)
    print('endb (a few seconds after enda)')


threads = [threading.Thread(target=blocking_funa), threading.Thread(target=blocking_funb)]
for thread in threads:
    thread.start()
for thread in threads:
    thread.join()

输出:

a 1
b 1
b 2
a 2
enda (quick)
endb (a few seconds after enda)

考虑到运行打印语句几乎不需要任何时间,您不应该过多地阅读第一个示例中的打印内容。

如果你反复运行代码,你可能会发现b 2a 2 会或多或少地随机改变顺序。请注意在我发布的结果中,b 2 是如何出现在 a 2 之前的。

另外,关于您的评论“由于 GIL 限制,同时只有一个线程正在执行”-您说得对,“任何 Python 字节码的执行都需要获取解释器锁。这可以防止死锁(因为那里只是一个锁)并且不会引入太多的性能开销。但它有效地使任何受 CPU 限制的 Python 程序成为单线程的。” https://realpython.com/python-gil/#the-impact-on-multi-threaded-python-programs

其中的重要部分是“受 CPU 限制” - 当然,您仍然会受益于使 I/O 受限制的代码多线程。

【讨论】:

    【解决方案2】:

    Python 经常在 GIL 上发布/获取。这意味着可运行的 GIL 控制线程都将获得很少的 sprint。它不是平行的,只是交错的。对您的示例更重要的是,python 在执行阻塞操作时倾向于释放 GIL。 GIL 在sleepprint 进入C 库之前发布。

    【讨论】:

      猜你喜欢
      • 2019-04-13
      • 1970-01-01
      • 2012-01-29
      • 2022-01-21
      • 1970-01-01
      • 2012-08-20
      • 1970-01-01
      • 1970-01-01
      • 2016-02-08
      相关资源
      最近更新 更多