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

弱极了,请教 Python 多线程如何快速开始

  •  
  •   pathetique · 300 天前 · 3861 次点击
    这是一个创建于 300 天前的主题,其中的信息可能已经有所发展或是发生改变。
    文科轻拍

    晚上用 python 写雍正实录的词汇统计

    雍正的起居官特别能写,一百三十多万字,用 M1 pro 的电脑,半个小时才检索到三万多字(算法应该没问题),看了看 cpu ,只有一个 100%的占用

    有没有特别简单的 python 多线程调用入门,希望自己能理解的那种?

    不知道问清楚没有
    53 条回复    2023-06-03 23:21:44 +08:00
    noneusername
        1
    noneusername  
       300 天前
    边看文档,边让 GPT 输出示例测试
    ysc3839
        2
    ysc3839  
       300 天前 via Android   ❤️ 1
    CPU 密集运算用 Python 的多线程是没用的,需要多进程
    这种情况手动分段然后开多几个同时跑就好了
    Weixiao0725
        3
    Weixiao0725  
       300 天前   ❤️ 1
    看一下 multiprocessing.Pool
    suith27
        4
    suith27  
       300 天前 via iPhone
    shalingye
        5
    shalingye  
       300 天前 via Android
    你需要的是多进程,简单得很,这是我的模板:
    if __name__=='__main__':
    from multiprocessing import Pool # 导入进程池
    path=choosefolder()
    p = Pool() # 创建进程池,留空代表 Cpu 的逻辑内核数目
    while True:
    try:
    for n in range(1,1000):
    p.apply_async(download, args=(n,path)) # 向进程池中添加任务
    except:
    p.close() # 结束向进程池中添加任务(后续不能再使用 apply_async 方法添加新任务)
    p.join() # 实现进程同步
    break
    shalingye
        6
    shalingye  
       300 天前 via Android
    缩进寄了,自己排吧,choosefolder 和 download 都是函数名,args 代表 download 函数的参数,如果只有一个参数需要写成 n,的形式,把你需要分割处理的函数替换到 download 的位置
    fyq
        7
    fyq  
       300 天前
    如果只有一个文档的话,最简单的方法是手动拆分成好几个,然后同时针对拆分后不同的的文档运行你的程序去分析,时候再针对得到的结果汇总一下就好了。
    liyafe1997
        8
    liyafe1997  
       300 天前   ❤️ 2
    Python 的多线程是假的,忘了这东西吧,要干正事得看多进程 multiprocessing.Pool
    lovelylain
        9
    lovelylain  
       300 天前 via Android
    python 有个著名的 GIL 锁,执行 Python 代码时都需要先获取这个锁,所以虽然多线程,但同一时间只有一个线程获得这个锁,其他线程都在等待,结果只能跑满一个核。如果你是 c 模块处理,可以手动释放锁处理完再获取,这样多线程就能突破单核限制。但 c 模块开发效率和可移植性很差,更好的方案是上面提到的 multiprocessing 多进程。
    tulongtou
        10
    tulongtou  
       300 天前
    卧槽,现在文科生都这么优秀了么,卷到理科生这边来了
    litguy
        11
    litguy  
       300 天前
    多进程,每个进程处理其中的一部分东西,最后所有进程结果汇总
    strawberrydafu
        12
    strawberrydafu  
       300 天前
    检索具体是在干什么?半小时才 3 万字我怀疑算法本身有很多优化空间
    Kinnice
        13
    Kinnice  
       300 天前 via Android   ❤️ 2
    词汇统计,m1 ,半小时 3w 字,看起来算法不像是没有问题
    ohayoo
        14
    ohayoo  
       300 天前
    其实可以用第三方的任务队列库,自带多进程多线程选项,只需要关注自己的逻辑函数即可,都可以尝试对比下
    adoni
        15
    adoni  
       300 天前
    参见: https://adoni.github.io/2019/01/07/python-practice/#concurrent-and-multi-process

    ```python
    from concurrent.futures import ProcessPoolExecutor
    with ProcessPoolExecutor(max_workers=16) as exe:
    result = exe.map(func, data_list)
    ```

    另外,你可以直接改成单线程,看一下,我感觉 130 万字,分分钟跑完。
    aijam
        16
    aijam  
       300 天前
    "半个小时才检索到三万多字",这得多慢啊,口算可能都比这个快
    coderluan
        17
    coderluan  
       300 天前
    这个你问 chatgpt 更方便,最简单应该是 concurrent.futures.ThreadPoolExecutor ,然后用 subprocess 调用你的程序开多进程。
    killva4624
        18
    killva4624  
       300 天前
    多进程记得考虑进程安全
    laqow
        19
    laqow  
       300 天前
    会不会算法其实有问题?至少 python 原生的文件 IO 和字符串处理慢的一匹,直接把所有文字 read 进内存再处理,或者以二进制读取后在 byte 对象上搜索都能快出个十倍来
    iX8NEGGn
        20
    iX8NEGGn  
       300 天前 via iPhone
    半小时三万多字,算法肯定有问题。百万字算法合适的话也,词频统计也就几十秒
    lyz1990
        21
    lyz1990  
       300 天前
    半小时 3 万慢得不太正常
    pathetique
        22
    pathetique  
    OP
       300 天前
    @Kinnice @Weixiao0725 @adoni @aijam @coderluan @fyq @killva4624 @laqow @litguy @liyafe1997

    非常感谢各位的帮助!就不一一回复,几位大佬我一起感谢先

    先非常感谢对多线程和进程的区分,我已经开始用 multiprocessing module 了,在我的 M1max 上基本上十个小时可以做完,对于雍正可以接受了。但是随着搜索对象正则式的复杂化我怀疑还要更久更久,而且雍正才干几年,乾隆的起居录估计有十倍大,可能个人电脑还是不够。

    然后几位大佬怀疑算法有没有问题,我也不知道哪里可以改善的。我把最简单的版本(算二字成词率)代码放在这里,辣大家的眼可能,但是真心请问如果有耐心看完的大佬:有没有改进的空间?如果是单线程的话,我可以从头到尾扫描,扫过的不再碰,复杂度是 n^2/2 ,但是因为多线程,把文本分成多块就没头没尾的,复杂度是 n^2 但是可以多核平均。


    checkdict = [] #已经查过的单词词库
    list = [] #文本本身
    step = 100000 #每个机器人负责多大块

    with open('yong3.txt', 'r') as file:
    contents = file.read()
    for chr in contents:
    if chr != '\n' and chr != ' ' and chr != '。' and chr != '、' and chr != '○' and chr != '\u3000': #有些奇怪的字符去掉
    list.append(chr)
    length = len(list) #雍正大概 100 万字


    import multiprocessing.pool #import the multiprocessing module

    def worker(num):
    """Worker function for the process"""
    print(f'Worker {num} starting')

    count = 0
    for i in range(0+step*num,min(step+step*num,length-2)): #每个机器人负责 #step 长度的文字
    if (list[i:i+2] not in checkdict): #如果不在字典里
    count = 0
    for j in range(0,length-2): #全本比对,因为分块似乎只能这么做,不能从 i 开始?
    if list[i:i+2] == list[j:j+2]:
    count = count + 1
    checkdict.append(list[i:i+2]) #check 完,添进已查字典
    if count > 350: #出现次数高于 350 次的字显示出来
    print (list[i:i+2])
    print(i, count, f"{num}th worker at percentage = {format((i/step-num)*100, '.2f')}%" )
    print(f'Worker {num} finished')


    还是挺好玩的,发现很多有趣的事实,比如弄死了年羹尧雍正还常提,比如他喜欢的十三弟其实没那么常提。我其实拿清史资料是因为手头方便,下一步更想用 Colibri Core 处理黏着语(黏着语,比中文不一样的地方在于一个词根会有很多变化,比如 love, loves, loving )。

    想问调用 Calobri Core 这样的库啊包的各位大佬还有什么建议呢?还是照着 ChatGPT 的做?(我的 Python 真的很生,刚学,小时候会用 c )

    然后想问下,如果有人有时间帮小的看了这个案例,这种算法用显卡( CUDA )来帮助做会有帮助吗?或者我下一步弄大了,比如一千万字的更加模糊的搜索(比如犹太注经动辄几千万),有什么好的建议提速呢?可以借学校的服务器或者云吗?但是感觉云的 CPU 频率应该也一般。

    非常感谢大家指路!小的刚刚用电脑编程对付这些,求拍求建议求更多工具。
    Kinnice
        23
    Kinnice  
       300 天前
    @pathetique #22 首先是做中文分词 eg: https://github.com/fxsjy/jieba ,然后应该可以非常快,提供个 datasource ?。
    julyclyde
        24
    julyclyde  
       300 天前
    分词这事有前后顺序依赖吧,能多线程??
    Anarchy
        25
    Anarchy  
       300 天前
    @julyclyde 按章节、段落、句子都可以啊,没人一句话写一本书的吧。
    zhzy
        26
    zhzy  
       300 天前
    看了一下,没做分词,直接每两个字作为一个词;
    用 list 循环判断是否存在,而且文本里没有重复的文字么,用两个 for 循环感觉没什么必要,相当于每个词都翻一遍全文,O(n^2),不慢才怪吧。其实直接一个循环然后字典里+1 就行了,虽然也怪怪的,但是至少比现在的快;
    其实还是建议不要自己写,或者至少了解一下这类算法应该怎么写...完全用自己的思路闷头搞有时候真的不容易意识到代码里的问题...要不还是老老实实上 NLP 库
    ispinfx
        27
    ispinfx  
       300 天前
    这么点体量多进程要跑 10 小时?
    zhzy
        28
    zhzy  
       300 天前
    @zhzy Python 不是 c ,很多东西不需要自己实现的,而且尽量不要用内置的类型作为变量名,快速糊了一个版本,应该会快一些。
    zhzy
        29
    zhzy  
       300 天前
    @zhzy #28 惊了,手快了,counter 那个循环里要判断一下有没有 key ,没有的话设置为 1 ,有的话加 1
    hellojukay
        30
    hellojukay  
       300 天前
    1. list 如果长度非常的长,可能话很多时间在增长扩容上,建议使用 linklist
    2. if xx in [] , 这种判断方式效率非常低,应该使用 hash 的方式
    pathetique
        31
    pathetique  
    OP
       300 天前
    @zhzy 谢过,我去试试用 panda !不用中文的分词 library 是因为用中文只是试试,其实主业是古代西方语言,最后需要 n-gram skipgram flexgram 之类的还是要自己微调很多自己写然后穷尽搜索。请问 regex 或者 panda 有好的入门建议吗?还,就 GPT 就好?
    pathetique
        32
    pathetique  
    OP
       300 天前
    @hellojukay 感恩,我马上学一下 linklist 和 hash……
    zhzy
        33
    zhzy  
       300 天前
    @pathetique #31 如果只是处理一下停用词的话就一两行代码,不需要学,抄一下就行,百万字其实很少的,只要避免循环嵌套这种性能问题不大的
    zhzy
        34
    zhzy  
       300 天前
    @zhzy #29 还是把这个补上吧,用 get 的默认值,不用 if in keys 判断
    ispinfx
        35
    ispinfx  
       300 天前
    不知道你是不是要 28 楼这种效果,100 万字跑了也就 0.几秒。
    ispinfx
        36
    ispinfx  
       300 天前
    @zhzy #34 直接 collections.Counter()就能直接加了
    liyanm169gd
        37
    liyanm169gd  
       300 天前
    猜一下 OP 的意思,如果是每两个字取下来然后统计频率可以这样做,假定你已经把文本读到了变量 tmpstr 里,total = [tmpstr[i]+tmpstr[i+1] for i in range(0,len(a)-1,2)],如果要统计可以直接 set ,然后拿 set 的值当 dict 的 key ,value 是 total.count("这里是 key")
    zhzy
        38
    zhzy  
       300 天前
    @ispinfx #36 是的,可以直接根据文本构造一个步长为 1 每个元素长度为 2 的 list ,然后 Counter 就行,不过那样逻辑就被隐藏掉了,OP 的代码主要问题是扫描 list 来计数,这里只是展示一下更合理的逻辑
    ispinfx
        39
    ispinfx  
       300 天前
    @zhzy #38 我的意思是用 Counter 你就不用 get 一下再加了。
    pathetique
        40
    pathetique  
    OP
       300 天前
    @zhzy 跑通了,非常感谢!但是您的代码我还在慢慢理解中……因为不太理解 counter.get 或者 counter.items 这样的命令。是需要去 pathlib 的档案找意思吗?
    pathetique
        41
    pathetique  
    OP
       300 天前
    @zhzy
    @ispinfx
    非常感谢
    pathetique
        42
    pathetique  
    OP
       300 天前
    再问一个白痴问题,我和您方案的最大差别是不是创建的对象是不是 hashable 的差别?大概就是 30 楼大哥说 list 的效率低,hash 效率高的问题?
    非常非常感谢两位的时间!知道大家都挺忙……
    pathetique
        43
    pathetique  
    OP
       300 天前
    奇怪为啥有些帖子回复发不出去说要注册 1001 天……1001nights 吗……
    zhzy
        44
    zhzy  
       300 天前
    @pathetique #40 pathlib 是一个文件系统路径库,封装了一些比较方便的功能,比如可以直接读取文本,用来代替 with...as f: f.read ;
    counter 是一个字典,对于一个字典对象 d ,d.items 是取出每一项的键(key)和值(value)
    for k, v in d.items()这个循环相当于逐一取出字典的每一项
    counter.get(key, defaultValue)实际上可以理解为 if key in counter.keys: counter[key]; else: return defaultValue, 作用是尝试根据键读取字典的一个值,如果不存在这样的键, 返回 defaultValue
    在 python 里其实这类方法很多, 可以节省很多代码量, 并且速度要比你自己写的逻辑快, 因为在底层它可能会做一些优化
    zhzy
        45
    zhzy  
       300 天前
    @ispinfx #39 啊 我明白您的意思了, 其实我的意思是 collections.Counter 是可以直接传一个可迭代对象或者 map 的, 比如传一个 str 或 list 进去就可以直接做计算
    aitianci
        46
    aitianci  
       300 天前
    @Kinnice #23 我玩了一下,用 jieba 出来的效果还不如 re 。我是从网上下载的一个 2MB 多的雍正实录,到 115 卷就没了。jieba 给出的全是单字词,像“朕,等,为,有”这种,re 能给“出谕内阁,应如所请,寻议,下部知之,入祠致祭如例,均应如所请,缘事革职,谕大学士等,给银建坊”这种还有点含义的词组。
    LaurelHarmon
        47
    LaurelHarmon  
       300 天前   ❤️ 1
    笑死,这点数据量,跟多线程、多进程没半毛钱关系,你还是先理清楚需求,然后改一下代码。正常情况秒秒钟出来,暴力循环不可取。
    pathetique
        48
    pathetique  
    OP
       300 天前
    @aitianci 嗯嗯,其实最大的价值、不是在于观察已有的成形的 pattern 或者单词,而恰恰是一些直观不熟悉但统计上又显著的词汇、语法结构或者说法。比如很多语言中动词-主语-宾语顺序多,但某些地方开始突然高密度调用其他语序;或者很多语言的果-因表达多,但如果突然用因-果连词;或者突然用佶屈聱牙的、甚至字典上没有的词,就很可能有很重要的、非母语者不容易看出的情绪或者信息。反常的信息最珍贵
    pathetique
        49
    pathetique  
    OP
       300 天前
    @zhzy 非常感谢,我去读下 pathlib 的文档~
    Deplay
        50
    Deplay  
       299 天前
    单从实现这个任务来讲,使用现成的 wheel 更好的选择,自己写还是很难的,例如#23 所提及的 jieba
    也可以考虑上 nlp ,nltk 应该就可以(这一块没怎么接触过,不是很清楚)
    至于你提到用 gpu 处理,理论上确实可以,但没必要,毕竟不是很复杂的任务,dl 没必要,学习成本过高
    julyclyde
        51
    julyclyde  
       299 天前
    @Anarchy 分章节、分段落是人工还是自动呢?如果是自动,那也得按顺序过一遍程序啊,这环节也是不能并发的吧
    phoulx
        52
    phoulx  
       299 天前
    #48 感觉 up 做的东西很有意思呢,之前专业相关,Python 也懂一点。看上去主楼问题已经解决了?如果仍有疑问,欢迎联系 https://paste.rs/Zb0gs ,也许可以提供一点帮助!
    klion
        53
    klion  
       298 天前
    就算文字有 1000 万,也不过不到 100M 的内存,这怎么会 3 万字跑半个小时啊,是不是你自己用了太多 for 做遍历了,看起来更像是你的代码需要优化,你去用 profile 逐行分析代码性能瓶颈再讨论吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3230 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 78ms · UTC 14:19 · PVG 22:19 · LAX 07:19 · JFK 10:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.