推荐学习书目
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 · Oct 22, 2019 · 3900 views
    This topic created in 2420 days ago, the information mentioned may be changed or developed.

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

    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 replies    2019-10-22 20:05:48 +08:00
    star00
        1
    star00  
       Oct 22, 2019
    开 5 个线程,每个线程循环从一个线程安全的队列抛个值,做处理就 ok 了
    Trim21
        2
    Trim21  
       Oct 22, 2019 via Android
    用的手机,懒得查 api 了,可能某些单词拼的是错的…

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

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

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

    doSomething 函数封装得很好,函数完全是独立的,不交叉到局外通信,while 到不完成不返回,超过重试次数就自己结束,有一个地方交叉,可能就是抛出异常的时候,会向"d:\error.log"写点东西,会担心同时多进程同时异常都往 error.log 写内容?
    ClericPy
        6
    ClericPy  
       Oct 22, 2019
    最简单的还是 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  
       Oct 22, 2019
    把字典转成 list,然后平分到多个线程中去执行,如果 dosomething 是 io 密集型,多线程效率肯定会提升
    Vegetable
        8
    Vegetable  
       Oct 22, 2019
    @qazwsxkevin windows 上的 multiprocessing 原理和 linux 不太一样,我搜了一下别人的文章,好像也可以用多进程,我刚接触 python 的时候被坑过后来没关注了,你可以自己了解一下怎么用。
    不启动多个进程,也就无法绕过 GIL 锁,不能利用多个核心,只能加速 io 密集型操作,在 linux 上是肯定可以启动多个进程的,如果计算任务足够重的话,系统会将任务分配给多核计算,能够跑满 cpu。
    zdt3476
        9
    zdt3476  
       Oct 22, 2019
    既然楼上已经回答了,说过题外话。 遍历。。。
    emmmbu
        10
    emmmbu  
       Oct 22, 2019
    上亿数据我的做法 分段扔线程安全的队列 启动任意多个线程从队列取一段数据,消费完再取 这样各个线程不相互影响啊
    qza1212
        11
    qza1212  
       Oct 22, 2019
    一般多线程用生产消费模型,临界区用队列就行

    假如非要用你说的字典这种,我推荐用 hash 分桶
    ps1aniuge
        12
    ps1aniuge  
       Oct 22, 2019
    $hash = @{
    a = 1
    b = 2
    #省略
    }

    $hash.GetEnumerator() | ForEach-Object -ThrottleLimit 2 -Parallel {
    #系统需求=powershell 7 preview4
    #你的代码
    #-ThrottleLimit 2 为并发数
    }
    qazwsxkevin
        13
    qazwsxkevin  
    OP
       Oct 22, 2019
    @Vegetable,感谢解答~
    @qza1212 ,是的,刚刚琢磨到,字典历遍方式似乎不太适合我这样放到线程队列里,正在琢磨其它办法。。。
    @ps1aniuge 学习 ing...
    ps1aniuge
        14
    ps1aniuge  
       Oct 22, 2019
    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  
       Oct 22, 2019
    你们可以去看看 Python 最好用的(非第三方)多线程类,这才是 Python 多线程的未来。就下面三行代码就可以实现你上面说到要求,就这么简单好用,而且这个多线程类是自带线程锁的,对于 I/O 密集型的线程操作,简直就是爽翻天。

    ```
    from concurrent.futures import ThreadPoolExecutor

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

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

    加上现在的集合来源,是字典,我本来在 for 的历遍里面是有一些 continue 拐弯的,现在也甚是头疼怎么改。。。
    xfriday
        18
    xfriday  
       Oct 22, 2019
    看楼主的需求是并行 doSomething 吧,可以开个 N(=5)个队列,然后遍历 map 往 N 个队列里 push,N 个线程从各自的队列里 pop 消费
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2947 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 59ms · UTC 13:06 · PVG 21:06 · LAX 06:06 · JFK 09:06
    ♥ Do have faith in what you're doing.