zoukankan      html  css  js  c++  java
  • 线程&线程池

    线程

    进程和线程:

      进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

    注意:两个都是过程

    线程一个特点:

      一个进程中,多个线程共享资源

    线程和进程区别:

      1. 线程的创建开销小于进程, 创建速度快

      2. 同一进程下的多个线程共享该进程的地址空间(资源)

    主线程影响其他线程:

      是因为线程的概念:

      一个进程内默认就会有一个控制线程,该控制线程可以执行代码从而创建新的线程,

      该控制线程的执行周期就代表该进程的执行周期

    1.一个进程内不开子进程,也不开'子线程' : 主线程结束,该进程就结束

    2.当一个进程内开启子进程时 : 主线程结束,主进程要等,等所有子进程运行完毕,给儿子收尸.

    3.当一个进程内开启多个线程时 :

      主线程结束并不意味着进程结束,

      进程的结束指的是该进程内所有的线程都运行完毕,才应该回收进程.

    简单点说,就是:

      主进程等子进程, 是主进程活都干完了

      主线程等子线程,是主进程还没有干完活

      (所有的线程都干完活了,该进程才算干完活了)

    子进程和线程的id号 验证内存空间

    import os
    from multiprocessing import Process
    
    n = 100
    def func():
        global n
        n = 1
        print('',n,os.getpid())
    
    
    if __name__ == '__main__':
        p = Process(target=func,)
        p.start()
        print('',n,os.getpid())
    # 主 100 8852        主进程和子进程的id号不一样,说明进程间的内存空间是隔离的
    # 子 1 4716          子进程修改变量,主进程感觉不到,说明进程间的内存空间是隔离的

    全局变量的修改,验证内存空间

    import os
    from threading import Thread
    
    n = 100
    
    
    def func():
        global n
        n = 1
        print(current_thread().getName(), n, os.getpid())
    
    
    if __name__ == '__main__':
        t = Thread(target=func,name = ‘func’)       #给子线程起名字,不写,默认 Thread-1.Thread-2.....
        t.start()
        print(current_thread().getName(), n, os.getpid())
        
    # func 1 1088          子线程和主线程的id号一样,说明主线程和子线程在一个进程空间里
    # MainThread 1 1088          子线程修改变量n = 1,主线程能感知到,说明线程间共享数据

    进程可以看进程号

    线程只能看名字了

    getName() 测试的时候用的较多

    enumerate()    以列表的形式列出当前所有活着的线程

    active_count()    统计当前所有活着的线程的个数

    import time
    from threading import Thread, active_count, enumerate       需要导入threading模块中这两个方法
    
    
    def func():
        time.sleep(3)
    
    
    if __name__ == '__main__':
        t = Thread(target=func)
        t.start()
        print(enumerate())
        print(active_count())
    
    # [<_MainThread(MainThread, started 9212)>, <Thread(Thread-1, started 1612)>]   列表形式
    # 2 
    
    -------------------------------------
    
    print(enumerate()[0].getName())         #enumerate()是一个列表,取索引值[0],再通过getName(),一样能拿到线程的名字
    print(current_thread().getName())
    #MainThread
    #MainThread

    os.cpu_count()    查看cpu核数

    import os
    print(os.cpu_count())

    线程池

    t = ThreadPoolExecutor()    创建线程池的时候,如果不设置数量,默认是cpu核数*5

    证明线程池不设置参数,默认值 是cpu核数*5

    import time
    import os
    from threading import ac-tive_count
    from concurrent.futures import ThreadPoolExecutor
    
    
    def func():
        time.sleep(1)
    
    
    if __name__ == '__main__':
        t = ThreadPoolExecutor()            #创建线程池的时候,如果不设置数量,默认是cpu核数*5
        for i in range(100):
            t.submit(func,)
        print(os.cpu_count())       #cpu核数
        print(active_count())       #子线程数 + 主线程
    
    #4
    #21   

    进程池不要超过cpu核数的2倍(不是真理,具体会根据程序问题的增多,数目相应调大,也和电脑的性能有关)

    这个参数肯定不能再程序里面写死,要写到配置文件里,要让软件的使用者( 给运维人员,不是直接给客户)根据他自己的业务场景以及他硬件的性能,要做相应的调整,取配置,所有的支

    持并发的软件都是这样做,都应该这样做.不应该在程序里面写死.

    多线程+异步调用+requests

    requests 库

    HTTP客户端库(编写爬虫和测试服务器响应数据时经常用到)

    安装:pip install requests 

    get 请求:

      r = requets.get('http://www.baidu.com')

      r.text 返回headers中的编码解析的结果

    异步调用:

      (回调机制:任务执行完,触发回调函数,同时把任务结果传给回调函数)

      提交完任务,(为该任务绑定一个回调函数),不用在原地等任务执行完毕拿到结果,可以直接提交下一个任务.

      一个任务一旦执行完毕就会自动触发回调函数的运行

    不在原地等结果,那么结果哪里去了?

      就是通过回调机制

    回调函数:add_done_callback(func) 

      回调函数的参数是单一的:

      回调函数的参数就是它所绑定任务的返回值(绑定任务的return)

    异步调用,不用等结果 结果去哪了 就是 回调

    import requests
    import time
    from threading import current_thread
    from concurrent.futures import ThreadPoolExecutor
    
    url = ['https://www.baidu.com',
           'https://www.jd.com',
           'https://www.python.org',
           'http://www.qq.com']
    
    
    def get(url):
        print('get',current_thread().getName(),url)     #current_thread().getName()打印哪些线程执行下载任务
        time.sleep(2)
        r = requests.get(url)
        if r.status_code == 200:                # 200代表成功下载
            return {'url': url, 'text': r.text}
    
    
    def parse(obj):
        res = obj.result()
        print('parse',current_thread().getName(),res['url'],len(res['text']))       #打印哪些线程执行解析任务
    
    
    if __name__ == '__main__':
        t = ThreadPoolExecutor(2)
        for i in url:
            t.submit(get,i).add_done_callback(parse)        #回调函数
        t.shutdown(wait=True)
        print(current_thread().getName())
    
    # get ThreadPoolExecutor-0_0 https://www.baidu.com
    # get ThreadPoolExecutor-0_1 https://www.jd.com
    # parse ThreadPoolExecutor-0_0 https://www.baidu.com 2443
    # get ThreadPoolExecutor-0_0 https://www.python.org
    # parse ThreadPoolExecutor-0_1 https://www.jd.com 124541
    # get ThreadPoolExecutor-0_1 http://www.qq.com
    # parse ThreadPoolExecutor-0_1 http://www.qq.com 235464
    # parse ThreadPoolExecutor-0_0 https://www.python.org 48856
    # MainThread

    一直是由线程控制处理

    通过进程演示异步概念(不用开新的进程,主进程处理结果)

    import requests
    import time
    import os
    from threading import Thread, current_thread
    from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
    
    url = ['https://www.baidu.com',
           'https://www.jd.com',
           'https://www.python.org',
           'http://www.qq.com']
    
    
    def get(url):
        print('get',os.getpid(),url)            #os.getpid()打印哪些进程执行下载任务
        time.sleep(3)
        r = requests.get(url)
        if r.status_code == 200:
            return {'url': url, 'text': r.text}
    
    
    def parse(obj):
        res = obj.result()
        print('parse',os.getpid(),res['url'],len(res['text']))      #os.getpid()打印哪些进程执行解析任务
    
    
    if __name__ == '__main__':
        t = ProcessPoolExecutor(2)
        for i in url:
            t.submit(get,i).add_done_callback(parse)
        t.shutdown(wait=True)
        print('
    ',os.getpid())
    
    # get 6972 https://www.baidu.com
    # get 7180 https://www.jd.com
    # get 6972 https://www.python.org
    # parse 7856 https://www.baidu.com 2443
    # get 7180 http://www.qq.com
    # parse 7856 https://www.jd.com 124541
    # parse 7856 http://www.qq.com 235464
    # parse 7856 https://www.python.org 48856
    #
    #  7856
  • 相关阅读:
    Postman几种常用方式
    PL/SQL 循环结构
    【oracle】解锁oracle用户,unlock
    四则运算题2
    有关Botton的用法(一)
    SQLiteOpenHelper的使用
    用Toast来增加调试效率的小技巧
    汇编语言-比较字符串
    正向代理和反向代理
    redis安装与配置
  • 原文地址:https://www.cnblogs.com/zhzhlong/p/9297258.html
Copyright © 2011-2022 走看看