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

2019-12-16 18:18:15 +08:00
 GreenJoson

按照手册实例结合自己想写的一个多访问的脚本,有了以下的代码.有 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)

3899 次点击
所在节点    Python
11 条回复
superrichman
2019-12-16 19:55:08 +08:00
GreenJoson
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
2019-12-16 20:18:40 +08:00
“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
2019-12-16 20:18:53 +08:00
@superrichman 然后做一些友善的提示,比如访问到哪个站点了...是否在线程里面不可以用 if 来判断?不然为何到最后多显示 3 条 print
ipwx
2019-12-16 20:20:27 +08:00
最后评论一下:我不明白你为什么会有搞一个全局对象做 for 循环调用函数传参对象的这么一个需求,不应该创建一个局部的临时变量做参数么。。。这才是你的 bug 根源,也是我这么多年没写过这种神奇代码的理由。
GreenJoson
2019-12-16 20:21:04 +08:00
@ipwx 一语点破..谢谢你..“for conData['mark'],conData['url'] in URLS.items():” ,conData 只是为了加入一个字典里面,然后传递给函数,让函数去跟另一个字典做交集处理..哈哈..只是想偷懒而己.~
谢谢你,我知道问题所在了.~
ipwx
2019-12-16 20:22:27 +08:00
@GreenJoson 这种偷懒写法,随便重构一下就是一堆 bug 的根源。。。
ClericPy
2019-12-16 20:27:32 +08:00
for conData['mark'], conData['url'] in URLS.items() 这倒霉用法把我吓一跳...

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

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

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

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

虽说大部分人不看设计模式随着经验也能自发产生免疫力, 但是有这种捷径类疫苗在, 可以比较快地让自己知道自己哪里不优雅, 最后忠告就是别迷信设计模式
GreenJoson
2019-12-16 21:03:09 +08:00
@ClericPy 谢谢你的忠告,我先看看你发的.感谢~

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/629559

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX