V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
snachx
V2EX  ›  Python

前一个帖子沉了,再问一次,关于 python 的 ThreadPoolExecutor 使用,是我理解错了吗?

  •  
  •   snachx ·
    snachx · 2016-07-26 10:01:46 +08:00 · 2948 次点击
    这是一个创建于 3048 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前几天问了一次,很快沉了,再问问试试...

    看 concurrent.futures 的文档,讲 ThreadPoolExecutor 的时候有一段

    def wait_on_future():
        f = executor.submit(pow, 5, 2)
        # This will never complete because there is only one worker thread and
        # it is executing this function.
        print(f.result())
    
    executor = ThreadPoolExecutor(max_workers=1)
    executor.submit(wait_on_future)
    

    看注释的意思应该是说如果 max_workers > 1 的话就不会有问题吧?可是为什么我把 max_workers 设为大于 1 的值, f 的状态还是一直卡在 pending 没输出结果呢? macOS 和 Linux 上都试过了。

    18 条回复    2016-07-26 14:30:40 +08:00
    IMRES
        1
    IMRES  
       2016-07-26 10:20:19 +08:00
    这个文档中的关于"deadlocks"的一个反面的例子……
    需要将最后一行改为`wait_on_future()`才是正确的。
    gnuth
        2
    gnuth  
       2016-07-26 10:43:06 +08:00
    https://github.com/python/cpython/blob/master/Lib/concurrent/futures/thread.py#L104-L114

    wait_on_future 里面的 submit 卡在获取 _shutdown_lock 上了。
    snachx
        3
    snachx  
    OP
       2016-07-26 10:50:26 +08:00
    @IMRES 我知道这里是讲 deadlocks ,但是根据注释,这个例子强调的是因为只有一个 worker ,所以会死锁,如果最后一行改为 wait_on_future(),即使 max_workers = 1 也不会有问题的啊
    yangtukun1412
        4
    yangtukun1412  
       2016-07-26 10:55:09 +08:00
    在 executor.submit(wait_on_future) 后面加上:

    while True:
    time.sleep(1)


    原因: 可以看下 concurrent/futures/thread.py 中的 _python_exit() 和 _worker() 两个函数.
    snachx
        5
    snachx  
    OP
       2016-07-26 11:01:30 +08:00
    @gnuth 试着调试了一下,并没有卡在获取_shutdown_lock 啊
    gnuth
        6
    gnuth  
       2016-07-26 11:27:47 +08:00
    @snachx 晕,我想错了。。
    SuperFashi
        7
    SuperFashi  
       2016-07-26 12:05:14 +08:00 via Android
    所以真的是为什么……我设的 worker 为 2 顺利运行啊……
    SuperFashi
        8
    SuperFashi  
       2016-07-26 12:05:46 +08:00 via Android
    楼上的都没有自己试一下吗?
    snachx
        9
    snachx  
    OP
       2016-07-26 12:15:21 +08:00 via iPhone
    @SuperFashi 我设置为 1 就不行

    用 @yangtukun1412 的方法睡一秒就行,不用循环,在想为什么
    snachx
        10
    snachx  
    OP
       2016-07-26 12:16:15 +08:00 via iPhone
    @SuperFashi 是不管设为几都不行,而且注释的意思是设为 1 肯定不行,不理解了
    ljbha007
        11
    ljbha007  
       2016-07-26 12:20:53 +08:00
    除了线程的锁 python 还有一个全局锁 GIL 可能跟这有关系 但是我不没用过这个类 所以不清楚
    SuperFashi
        12
    SuperFashi  
       2016-07-26 12:28:12 +08:00 via Android
    @snachx
    上图。
    snachx
        13
    snachx  
    OP
       2016-07-26 12:43:08 +08:00
    @SuperFashi 我试了一下在 python shell 下面这么做确实没有问题,你试试写到文件里面,然后 python xxx.py 呢,你在 python shell 下面做跟前面说的睡一秒应该是一个意思吧
    yangtukun1412
        14
    yangtukun1412  
       2016-07-26 13:15:13 +08:00   ❤️ 1
    @snachx 大概说下吧

    1. 设 executor.submit(wait_on_future) 启动的是线程 1, executor.submit(pow, 5, 2) 启动的是线程 2.
    2. executor.submit(wait_on_future) 是非阻塞的, 所以在执行后主线程会退出.
    3. 由于 thread.py 中注册了 _python_exit(), 所以会在主线程退出前执行这个函数, 1) 设置 _shutdown = True, 2) 向队列中 put 一个 None, 3) 阻塞在 t.join(sys.maxint) 这里等待全部子线程退出.
    4. 线程 1 阻塞在 _worker() 中 work_item.run() 这一行, 因此 t.join(sys.maxint) 也会阻塞住, 阻止主线程退出.
    5. 线程 2 从队列中 get 到的并不是 pow 函数对应的 work_item, 而是 _python_exit() 中 put 的 None, 再加上 shutdown == True, 所以线程 2 会不做任何操作直接退出.
    6. 由于线程 2 已经退出, 所以 pow 函数没有被执行, 因此线程 1 也就永远被阻塞住了.

    所以出现这个问题的原因就是主线程提前退出, 用 sleep 等方式阻止主线程退出就可以解决了, 你改成 executor.submit(wait_on_future).result() 也是一样 OK 的.
    SuperFashi
        15
    SuperFashi  
       2016-07-26 13:17:44 +08:00 via Android
    @snachx @yangtukun1412
    这位说得清晰明了,当然我推荐阻塞方式为 executor.shutdown(wait=True)
    SuperFashi
        16
    SuperFashi  
       2016-07-26 13:19:59 +08:00 via Android
    啊 记错了 抱歉 用 result 阻塞就好了
    snachx
        17
    snachx  
    OP
       2016-07-26 13:39:44 +08:00
    @yangtukun1412
    之前已经知道了是主线程提前退出造成的,不过还没搞清楚具体流程。现在明白了,非常感谢~
    IMRES
        18
    IMRES  
       2016-07-26 14:30:40 +08:00
    @snachx 不好意思 是我搞错了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2637 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 01:56 · PVG 09:56 · LAX 17:56 · JFK 20:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.