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
latteczy
V2EX  ›  Python

Python 多线程问题

  •  1
     
  •   latteczy · 2015-07-07 20:20:58 +08:00 · 4032 次点击
    这是一个创建于 3212 天前的主题,其中的信息可能已经有所发展或是发生改变。


    上面的代码分别用多线程和普通方式抓取页面信息。但是二者运行时间相差无几,大约都是0.7+秒。这里应该不是CPU密集型,所以多线程应该有效率的提高才对。不知是何原因?
    最近在学习Python的多线程,请大神多多指教!
    28 条回复    2015-07-13 08:30:29 +08:00
    realityone
        1
    realityone  
       2015-07-07 20:28:15 +08:00
    你在 q 里面再加一句 time.sleep(5) 然后观察下就知道问题在哪里了



    `t.join()`
    RichardZhiming
        2
    RichardZhiming  
       2015-07-07 20:32:02 +08:00 via Android
    尽量使用多进程,gil是Python的历史遗留问题
    latteczy
        3
    latteczy  
    OP
       2015-07-07 20:36:14 +08:00
    @RichardZhiming 嗯,只是想搞明白这个Python的多线程是怎么回事
    zhyu
        4
    zhyu  
       2015-07-07 20:36:55 +08:00
    join([timeout])
    Wait until the thread terminates. This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception – or until the optional timeout occurs.
    latteczy
        5
    latteczy  
    OP
       2015-07-07 20:40:32 +08:00
    @realityone 加上time.sleep(5)之后结果是21秒多,还是不太明白,麻烦能否再指点一下。是t.join()有问题么?
    latteczy
        6
    latteczy  
    OP
       2015-07-07 20:43:09 +08:00
    @zhyu 额,join()是要等待该线程结束才能再起其他的线程啊。也就是说加上join()就跟单线程一样咯?
    Shazoo
        7
    Shazoo  
       2015-07-07 20:43:33 +08:00
    threads = []
    for i in range(4):
    t = threading.Thread(target=q, args=(hosts[i],))
    threads.append(t)
    t.start()

    for t in threads:
    t.join()

    这样应该会快些。
    alexapollo
        9
    alexapollo  
       2015-07-07 20:56:35 +08:00
    建议你用多进程 multiprocessing 模块。
    python 的线程机制比较弱,一般用多进程或异步解决这个问题。
    latteczy
        10
    latteczy  
    OP
       2015-07-07 21:02:54 +08:00
    @alexapollo 我知道,我只是想弄明白Python的多线程是怎么回事。而且对于这段代码,多线程应该是会有效率上的提高的,但是我并没测试出有提高。所以想弄明白是怎么回事。
    alexapollo
        11
    alexapollo  
       2015-07-07 21:08:03 +08:00
    @latteczy GIL,查了就懂了。活用google,SO上这种问题蛮多的
    latteczy
        12
    latteczy  
    OP
       2015-07-07 21:19:50 +08:00
    @alexapollo 嗯,这段代码效率没提高就是楼上所说的t.join()位置不对。主要原因不在GIL吧
    alexapollo
        13
    alexapollo  
       2015-07-07 21:22:53 +08:00
    @latteczy 恩,是的
    est
        14
    est  
       2015-07-07 21:27:07 +08:00
    GIL 真是个活靶子。
    latteczy
        15
    latteczy  
    OP
       2015-07-07 21:39:25 +08:00
    @Shazoo 感谢!确实是这样。但是又有个问题是代码改正以后貌似没法统计运行时间了。。
    https://gist.github.com/smartczy/35e692064b775dcc3e17#comment-1487649
    uniquecolesmith
        16
    uniquecolesmith  
       2015-07-07 21:44:00 +08:00
    gevent 用起来
    aec4d
        17
    aec4d  
       2015-07-07 22:06:43 +08:00
    这TM关GIL毛事 简直躺枪
    要记住 断然给你提GIL的 要么是高手 要么是SB 有GIL还有multiprocessing呢 多线程最需要注意的应该是线程安全
    这是threading.join的文档
    join([timeout])
    Wait until the thread terminates. This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception – or until the optional timeout occurs.

    When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call isAlive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out.

    When the timeout argument is not present or None, the operation will block until the thread terminates.

    A thread can be join()ed many times.

    join() raises a RuntimeError if an attempt is made to join the current thread as that would cause a deadlock. It is also an error to join() a thread before it has been started and attempts to do so raises the same exception.

    看了之后你就发现 在上面加上t.join就和下面遍历的写法实质上是一模一样的.
    一般网上很多教程给你加上setDaemon 加上join 你可以试着不加看是什么样的。等你熟悉了之后你就晓得那时候该用这些 哪时候不该用
    zhyu
        18
    zhyu  
       2015-07-07 22:59:29 +08:00 via iPhone
    @latteczy
    试试不要join
    latteczy
        19
    latteczy  
    OP
       2015-07-08 08:27:41 +08:00
    @aec4d 多谢大神指点!大神莫激动。。。
    fanta
        20
    fanta  
       2015-07-08 09:32:06 +08:00
    aec4d 说的对,每个连接是0.7s时,问题已不是多线程的问题了, 是网络(服务器链路)问题.
    Shazoo
        21
    Shazoo  
       2015-07-08 10:08:30 +08:00
    @latteczy

    如果需要统计时间,我觉得你可以在线程内部实现的。退出前打印下即可。如果要保证和之前类似的输出,就需要考虑在主线程内阻塞等待了。
    latteczy
        22
    latteczy  
    OP
       2015-07-08 14:41:34 +08:00
    @Shazoo 也就是说,保证按之前的请求顺序输出和提高效率二者始终是矛盾的,这么理解对吧?
    yuyang
        23
    yuyang  
       2015-07-08 14:54:33 +08:00
    @aec4d 网上一票SB只要看到别人用python写多线程程序,立马就喷,立马搬出GIL,立马要你改成多进程,以前我还会吐槽下这些不懂装懂的家伙,现在直接无视..
    sampeng
        24
    sampeng  
       2015-07-08 16:43:55 +08:00
    不是所有的多线程一定带来效益的提升。。。。。尤其是里面有网络请求这样的东西的时候。
    线程同步会坑很大一堆时间,完全和多线程带来的收益抵消甚至更严重的问题。

    个人看法,多线程只用在相互之间毫无关系,一丁点关系都没有。不需要同步状态。没有顺序关联的时候是最有效的。其他时候,纯粹是自己给自己找麻烦(线程同步很蛋疼的)
    ryd994
        25
    ryd994  
       2015-07-08 17:56:00 +08:00 via Android
    @sampeng 这种情况还好吧……加锁就可以了
    latteczy
        26
    latteczy  
    OP
       2015-07-10 08:35:11 +08:00
    @Shazoo
    这个地方不加join和加
    for t in threads:
    t.join()
    效果应该是一样的吧
    qqblog
        27
    qqblog  
       2015-07-11 14:26:33 +08:00
    计划今后生产、开发环境换成pypy,版本未定
    Shazoo
        28
    Shazoo  
       2015-07-13 08:30:29 +08:00
    @latteczy 也不矛盾了。你如果需要得到类似顺序执行的结果,就需要线程之间同步。同步方法有很多,这个情况下,比较简单的就是每个线程都记录下自己的执行时间,然后所有线程结束后,比较下,输出最长即可。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4628 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 10:07 · PVG 18:07 · LAX 03:07 · JFK 06:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.