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

如何多线程(可控数量)历遍完字典?

  •  
  •   qazwsxkevin · 2019-10-22 10:35:37 +08:00 · 3391 次点击
    这是一个创建于 1895 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有一个字典,原本穷举历遍的,

    ResultDict = {xxx,xxx,...}
    
    def ProcessID(caseID):
        doSomething(caseID)
    
    for eachID in ResultDict:
        doSomething(eachID)
        pass
    

    单线程运行太慢了,
    想作改动,控制 doSomething(eachID),同时进行 5 个线程(以后可能不止 5 个)去历遍完 ResultDict,多线程的教学,和简单测试代码我简单学习,运行过,
    这里有个我不懂的多线程解决算法是:如果严格控制同时只能 5 个,第 6 个开始介入,必须要等前面 1 个结束了才能开始开始,( doSomething(eachID)处理时间都是不可预计的,总而言之只能同时 5 个,逐个按顺序压入 doSomething 处理),是不是“栈”的概念?
    这样的线程控制算法,我简单搜了一下网上,似乎没有现成例子,高手们能讲解一下如何做到吗?
    感谢感谢~~

    18 条回复    2019-10-22 20:05:48 +08:00
    star00
        1
    star00  
       2019-10-22 10:42:17 +08:00
    开 5 个线程,每个线程循环从一个线程安全的队列抛个值,做处理就 ok 了
    Trim21
        2
    Trim21  
       2019-10-22 10:45:42 +08:00 via Android
    用的手机,懒得查 api 了,可能某些单词拼的是错的…

    你可以用 multiprocess.pool 里面的 threadpool,开一个最大数量为 5 的线程池,然后用 pool.map (或者 mapstar ),去作用在 resultdict.items ()上面
    Vegetable
        3
    Vegetable  
       2019-10-22 10:45:52 +08:00
    先说方法,multiprocessing.Pool.map 方法可以满足你的需求,不过需要解决好多线程之间的通信问题,并且 windows 上好像不能使用 multiprocessing 多线程,只能用协程,就没有意义了
    scukmh
        4
    scukmh  
       2019-10-22 10:45:59 +08:00
    doSomething 确定是 io 密集嘛?
    qazwsxkevin
        5
    qazwsxkevin  
    OP
       2019-10-22 11:01:41 +08:00
    感谢大伙热心的回复,先挑一些疑问简单回帖了先,手上还有其它事在忙着 ^_^

    @Vegetable,是在 Win 的环境下作业的,那么多了线程,历遍的效率总会有提高的吧?

    @scukmh,磁盘读写么? 不多,doSomething 就是轮流去生成几个 10 几 kb 的文本,读几个 1MB,2MB 大小的文件,下载几个网页(这个就是要等爬虫的返回时间,不确定时间点就在这里)

    doSomething 函数封装得很好,函数完全是独立的,不交叉到局外通信,while 到不完成不返回,超过重试次数就自己结束,有一个地方交叉,可能就是抛出异常的时候,会向"d:\error.log"写点东西,会担心同时多进程同时异常都往 error.log 写内容?
    ClericPy
        6
    ClericPy  
       2019-10-22 11:04:08 +08:00
    最简单的还是 multiprocessing.dummy 里面的线程池, 可以 map 也可以自己调度, 符合楼主说的只能 5 并发, 如果不是 IO 密集的, 把 dummy 去掉就是多进程...

    from multiprocessing.dummy import Pool


    def do(sth):
    print(sth)
    return sth


    pool = Pool(5)
    tasks = [i for i in range(20)]
    result = pool.map(do, tasks)

    print(result)

    这个目前信息看起来不像是栈
    kaiser1992
        7
    kaiser1992  
       2019-10-22 11:08:46 +08:00
    把字典转成 list,然后平分到多个线程中去执行,如果 dosomething 是 io 密集型,多线程效率肯定会提升
    Vegetable
        8
    Vegetable  
       2019-10-22 11:20:22 +08:00
    @qazwsxkevin windows 上的 multiprocessing 原理和 linux 不太一样,我搜了一下别人的文章,好像也可以用多进程,我刚接触 python 的时候被坑过后来没关注了,你可以自己了解一下怎么用。
    不启动多个进程,也就无法绕过 GIL 锁,不能利用多个核心,只能加速 io 密集型操作,在 linux 上是肯定可以启动多个进程的,如果计算任务足够重的话,系统会将任务分配给多核计算,能够跑满 cpu。
    zdt3476
        9
    zdt3476  
       2019-10-22 11:47:37 +08:00
    既然楼上已经回答了,说过题外话。 遍历。。。
    emmmbu
        10
    emmmbu  
       2019-10-22 11:51:24 +08:00
    上亿数据我的做法 分段扔线程安全的队列 启动任意多个线程从队列取一段数据,消费完再取 这样各个线程不相互影响啊
    qza1212
        11
    qza1212  
       2019-10-22 14:28:55 +08:00
    一般多线程用生产消费模型,临界区用队列就行

    假如非要用你说的字典这种,我推荐用 hash 分桶
    ps1aniuge
        12
    ps1aniuge  
       2019-10-22 14:42:53 +08:00
    $hash = @{
    a = 1
    b = 2
    #省略
    }

    $hash.GetEnumerator() | ForEach-Object -ThrottleLimit 2 -Parallel {
    #系统需求=powershell 7 preview4
    #你的代码
    #-ThrottleLimit 2 为并发数
    }
    qazwsxkevin
        13
    qazwsxkevin  
    OP
       2019-10-22 14:49:16 +08:00
    @Vegetable,感谢解答~
    @qza1212 ,是的,刚刚琢磨到,字典历遍方式似乎不太适合我这样放到线程队列里,正在琢磨其它办法。。。
    @ps1aniuge 学习 ing...
    ps1aniuge
        14
    ps1aniuge  
       2019-10-22 15:03:27 +08:00
    1 这个 hash 是非线程安全的,只能读。
    2 当然也有线程安全的 hash。
    https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=netframework-4.8

    3powershell 基于。net,多线程可以用多核。
    Hopetree
        15
    Hopetree  
       2019-10-22 15:50:35 +08:00
    你们可以去看看 Python 最好用的(非第三方)多线程类,这才是 Python 多线程的未来。就下面三行代码就可以实现你上面说到要求,就这么简单好用,而且这个多线程类是自带线程锁的,对于 I/O 密集型的线程操作,简直就是爽翻天。

    ```
    from concurrent.futures import ThreadPoolExecutor

    with ThreadPoolExecutor(max_workers=5) as pool:
    pool.map(dosomething, ResultDict.keys())
    ```
    robinlovemaggie
        16
    robinlovemaggie  
       2019-10-22 15:58:31 +08:00
    注意审题,强调要求“同时”的情况,楼上的所有回答都不及格吧~
    qazwsxkevin
        17
    qazwsxkevin  
    OP
       2019-10-22 16:27:28 +08:00
    @robinlovemaggie 我这两天自己学习了一下多线程,还不够深入,尽管上面高手们都提到了解决方法,其实一次取 5 个,每次 5 个这样压入函数进行独立线程处理,等 5 个处理完,再下一批,也是可以的,起码比起单线程处理要好多了。。。

    细想了一下,保持 5 个,出 1 个进 1 个的 FIFO 队列算法,太难了,我这里的情况可能要连整个处理函数的设计都要改。。。

    加上现在的集合来源,是字典,我本来在 for 的历遍里面是有一些 continue 拐弯的,现在也甚是头疼怎么改。。。
    xfriday
        18
    xfriday  
       2019-10-22 20:05:48 +08:00
    看楼主的需求是并行 doSomething 吧,可以开个 N(=5)个队列,然后遍历 map 往 N 个队列里 push,N 个线程从各自的队列里 pop 消费
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2722 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 08:41 · PVG 16:41 · LAX 00:41 · JFK 03:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.