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

Python 刚学, 纠结于多线程 concurrent.futures 的错乱的问题,请教下各位?

  •  
  •   GreenJoson · 2019-12-16 18:18:15 +08:00 · 3912 次点击
    这是一个创建于 1810 天前的主题,其中的信息可能已经有所发展或是发生改变。

    按照手册实例结合自己想写的一个多访问的脚本,有了以下的代码.有 2 种写法的: 第 1 种速度快很多,但是出现多个 print 输出,实在想不出怎么冒出来的.是线程出错还是线程多余的.我开的 5 个线程,print(future_to_url) 对象也是 5 个.但是跑起来就多了出 3 个,而且文字还错乱了.

    第 2 种暂时没有发现问题,求指教第一种为何会出现这种问题?

    URLS = {'baidu':'http://www.baidu.com',
            'sogou':'http://www.sogou.com',
            'so':'http://www.so.com',
            'youku':'http://www.youku.com',
            'qq':'http://www.qq.com'
    }
    
    def load_url(s,args):
        weburl = args['url']
        if args['mark'] == 'baidu':
            print(s + "百度访问:" + weburl)
            rs = requests.get(weburl)
        if args['mark'] == 'so':
            print(s + "so:" + weburl)
            rs = requests.get(weburl)
        if args['mark'] == 'sogou':
            print(s + "搜狗访问:" + weburl)
            rs = requests.get(weburl)
        if args['mark'] == 'qq':
            print(s + "腾讯访问:" + weburl)
            rs = requests.get(weburl)
        if args['mark'] == 'youku':
            print(s + "优酷访问:" + weburl)
            rs = requests.get(weburl)
        return str(rs.status_code)
    title = '速度与激情'
    conData ={'year':'2015','actor':'范·迪塞尔,保罗·沃克,杰森·斯坦森,米歇尔·罗德里格兹','subname':'狂野时速 7'}
    aa = []
    start = datetime.datetime.now()
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    
        # --------------------------------第 1 种.写法----------------------------------------------#
        future_to_url = {executor.submit(load_url,title,conData): (conData['mark'] ,conData['url']) for conData['mark'],conData['url'] in URLS.items()}
        print(future_to_url)
        for future in concurrent.futures.as_completed(future_to_url):
            url = future_to_url[future] #调用对应的 URL
            try:
                data = future.result() #获得函数返回的值
            except Exception as exc:
                print('%r generated an exception: %s' % (conData['url'], exc))#调试异常错误
            else:
                aa.append(data)
    
        #--------------------------------第 2 种.写法----------------------------------------------#
        # for conData['mark'],conData['url'] in URLS.items():
        #     future = executor.submit(load_url, title, conData)
        #     print(future)
        #     try:
        #         data = future.result() #获得函数返回的值
        #     except Exception as exc:
        #         print('%r generated an exception: %s' % (conData['url'], exc))#调试异常错误
        #     else:
        #         aa.append(data)
    
        executor.shutdown(wait=True)
    end = datetime.datetime.now()
    print (end-start)
    

    Snipaste_2019-12-16_17-42-54.jpg

    11 条回复    2019-12-16 21:03:09 +08:00
    superrichman
        1
    superrichman  
       2019-12-16 19:55:08 +08:00   ❤️ 1
    GreenJoson
        2
    GreenJoson  
    OP
       2019-12-16 20:16:59 +08:00
    @superrichman 谢谢大佬,但是这个结果可能不是我想要的.
    URLS = {'baidu': ('http://www.baidu.com', '百度'), 这块 key 是为了识别,然后对应不同的网页规则,取不同的内容.
    我只是示例一下.这个.
    title = '速度与激情'
    conData = {'year': '2015', 'actor': '范·迪塞尔,保罗·沃克,杰森·斯坦森,米歇尔·罗德里格兹', 'subname': '狂野时速 7'}
    我之所以多了这一个,是因为这个字典变量是固定的,我需要加入这块字典去与获取的网址内容做对比.

    def load_url(s,args):
    weburl = args['url']
    if args['mark'] == 'baidu':
    print(s + "百度访问:" + weburl)
    rs = requests.get(weburl)
    if args['mark'] == 'so':
    print(s + "so:" + weburl)
    rs = requests.get(weburl)
    if args['mark'] == 'sogou':
    print(s + "搜狗访问:" + weburl)
    rs = requests.get(weburl)
    if args['mark'] == 'qq':
    print(s + "腾讯访问:" + weburl)
    rs = requests.get(weburl)
    if args['mark'] == 'youku':
    print(s + "优酷访问:" + weburl)
    rs = requests.get(weburl)
    return str(rs.status_code)

    这块主要是想判断不同的 mark 然后 get 不同的网站取相关内容.

    可否告知我那个问题滥用的地方在哪里,谢谢~
    ipwx
        3
    ipwx  
       2019-12-16 20:18:40 +08:00   ❤️ 1
    “for conData['mark'],conData['url'] in URLS.items():”

    你不觉得这种用法很别扭么。。
    ----

    首先,文字错乱是正常的。因为多线程,print 是同时执行的。你要保证 print 不错乱,得加锁,或者干脆收集到同一个线程里面去 print。

    其次,我告诉你为啥会多出三项。就是因为你这个蹩脚的 for conData['mark'], conData['url']。说实话我很惊讶,因为我用 Python 这么多年,我都知不道 Python 能这么写。。。 从语义上看,这个写法的含义是把 URLS.items() 里面 for 出来的东西直接赋值到 conData 的这两个 entry 上面。

    但是啊,少年,conData 只有一个哟,但你的线程有五个诶~ 所以你某个线程在执行某一个 if args['mark'] == 'xxx': 里面的 requests.get ,执行完以后,突然在下一个 if args['mark'] == 'yyy': 的时候,args['mark'] 就已经变化了,它发现又匹配了,就又执行了一遍。
    GreenJoson
        4
    GreenJoson  
    OP
       2019-12-16 20:18:53 +08:00
    @superrichman 然后做一些友善的提示,比如访问到哪个站点了...是否在线程里面不可以用 if 来判断?不然为何到最后多显示 3 条 print
    ipwx
        5
    ipwx  
       2019-12-16 20:20:27 +08:00
    最后评论一下:我不明白你为什么会有搞一个全局对象做 for 循环调用函数传参对象的这么一个需求,不应该创建一个局部的临时变量做参数么。。。这才是你的 bug 根源,也是我这么多年没写过这种神奇代码的理由。
    GreenJoson
        6
    GreenJoson  
    OP
       2019-12-16 20:21:04 +08:00
    @ipwx 一语点破..谢谢你..“for conData['mark'],conData['url'] in URLS.items():” ,conData 只是为了加入一个字典里面,然后传递给函数,让函数去跟另一个字典做交集处理..哈哈..只是想偷懒而己.~
    谢谢你,我知道问题所在了.~
    ipwx
        7
    ipwx  
       2019-12-16 20:22:27 +08:00
    @GreenJoson 这种偷懒写法,随便重构一下就是一堆 bug 的根源。。。
    ClericPy
        8
    ClericPy  
       2019-12-16 20:27:32 +08:00
    for conData['mark'], conData['url'] in URLS.items() 这倒霉用法把我吓一跳...

    建议多看看软件设计的原则那些东西, 不求全懂, 得会点常识, 至少把单一职责和高内聚低耦合做好.

    祝早日写出让人读的赏心悦目的代码...
    GreenJoson
        9
    GreenJoson  
    OP
       2019-12-16 20:34:17 +08:00
    @ClericPy @ipwx 谢谢大佬的虚拟教诲~ 有没有相关的书籍可以推荐或者视频,可以学习一下~
    ClericPy
        10
    ClericPy  
       2019-12-16 20:39:41 +08:00
    @GreenJoson #9

    我可不是大佬, 最近也在从头看设计模式, 自己写了太多屎山, 不过现在搜到的博客讲的都各说各有理...

    大部分设计原则还是学设计模式时候里面捎带的, 我以前看的酷壳写的那个 https://coolshell.cn/articles/4535.html 不过比较旧. 要么就是随手翻的四人帮那个设计模式

    虽说大部分人不看设计模式随着经验也能自发产生免疫力, 但是有这种捷径类疫苗在, 可以比较快地让自己知道自己哪里不优雅, 最后忠告就是别迷信设计模式
    GreenJoson
        11
    GreenJoson  
    OP
       2019-12-16 21:03:09 +08:00
    @ClericPy 谢谢你的忠告,我先看看你发的.感谢~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2805 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 09:39 · PVG 17:39 · LAX 01:39 · JFK 04:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.