• 06.线程


    01.线程.线程 

    1)线程是操作系统调度的最小单位
    2)线程是进程正真的执行者,是一些指令的集合(进程资源的拥有者)
    3)同一个进程下的多个线程共享内存空间,数据直接访问(数据共享)
    4)为了保证数据安全,必须使用线程锁

    说明:下面利用for循环同时启动50个线程并行执行,执行时间是3秒而不是所有线程执行时间的总和

    import threading
    import time
    
    def sayhi(num): #定义每个线程要运行的函数
        print("running on number:%s" %num)
        time.sleep(3)
    for i in range(50):
        t = threading.Thread(target=sayhi,args=('t-%s'%i,))
        t.start()

    1.2 GIL锁和线程锁

    GIL全局解释器锁
    1、在python全局解释器下,保证同一时间只有一个线程运行
    2、防止多个线程都修改数据
    线程锁(互斥锁)
    1、GIL锁只能保证同一时间只能有一个线程对某个资源操作,但当上一个线程还未执行完毕时可能就会释放GIL,其他线程就可以操作了
    线程锁本质把线程中的数据加了一把互斥锁
    1、加上线程锁之后所有其他线程,读都不能读这个数据
    有了GIL全局解释器锁为什么还需要线程锁
    1、因为cpu是分时使用的在有GIL的情况下执行 count = count + 1 会出错原因解析,用线程锁解决方法在有GIL的情况下执行 count = count + 1 会出错原因解析,用线程锁解决方法
    • 在有L的情况下执行 count = count + 1 会出错原因解析,用线程锁解决方法
    # 1)第一步:count = 0   count初始值为0
    # 2)第二步:线程1要执行对count加1的操作首先申请GIL全局解释器锁
    # 3)第三步:调用操作系统原生线程在操作系统中执行
    # 4)第四步:count加1还未执行完毕,时间到了被要求释放GIL
    # 5)第五步:线程1释放了GIL后线程2此时也要对count进行操作,此时线程1还未执行完,所以count还是0
    # 6)第六步:线程2此时拿到count = 0后也要对count进行加1操作,假如线程2执行很快,一次就完成了
    #    count加1的操作,那么count此时就从0变成了1
    # 7)第七步:线程2执行完加1后就赋值count=1并释放GIL
    # 8)第八步:线程2执行完后cpu又交给了线程1,线程1根据上下文继续执行count加1操作,先拿到GIL
    #    锁,完成加1操作,由于线程1先拿到的数据count=0,执行完加1后结果还是1
    # 9)第九步:线程1将count=1在次赋值给count并释放GIL锁,此时连个线程都对数据加1,但是值最终是1
    死锁定义
    两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去

    1.3 多线程或者线程池

    线程有哪些模块?
    线程池有哪些模块?

    1.4 join()和setDaemon()

    2.4.1 join()
    实现所有线程都执行结束后再执行主线程
    import threading
    import time
    start_time = time.time()
    
    def sayhi(num): #定义每个线程要运行的函数
        print("running on number:%s" %num)
        time.sleep(3)
    
    t_objs = []    #将进程实例对象存储在这个列表中
    for i in range(50):
        t = threading.Thread(target=sayhi,args=('t-%s'%i,))
        t.start()          #启动一个线程,程序不会阻塞
        t_objs.append(t)
    print(threading.active_count())    #打印当前活跃进程数量
    for t in t_objs: #利用for循环等待上面50个进程全部结束
        t.join()     #阻塞某个程序
    print(threading.current_thread())    #打印执行这个命令进程
    
    print("----------------all threads has finished.....")
    print(threading.active_count())
    print('cost time:',time.time() - start_time)

    2.4.2 setDaemon()

    守护线程,主线程退出时,需要子线程随主线程退出
    import threading
    import time
    start_time = time.time()
    
    def sayhi(num): #定义每个线程要运行的函数
        print("running on number:%s" %num)
        time.sleep(3)
    for i in range(50):
        t = threading.Thread(target=sayhi,args=('t-%s'%i,))
        t.setDaemon(True)  #把当前线程变成守护线程,必须在t.start()前设置
        t.start()          #启动一个线程,程序不会阻塞
    print('cost time:',time.time() - start_time)

    1.5 Python中使用过的线程模块?

    1.5.1 threading
    Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。
    thread和threading模块允许程序员创建和管理线程。
    
    
    
    import threading
    import time
    
    def sayhi(num): #定义每个线程要运行的函数
        print("running on number:%s" %num)
        time.sleep(3)
        
    for i in range(50):
        t = threading.Thread(target=sayhi,args=('t-%s'%i,))
        t.start()

    1.5.2 concurrent.futures

    1、简介
    1、Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码
    2、但是当项目达到一定的规模,频繁创建/销毁进程或者线程是非常消耗资源的,这个时候我们就要编写自己的线程池/进程池,以空间换时间。
    3、但从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提供了ThreadPoolExecutor和ProcessPoolExecutor两个类,
    4、实现了对threading和multiprocessing的进一步抽象,对编写线程池/进程池提供了直接的支持。

    2、Executor和Future

    1. Executor

    1、concurrent.futures模块的基础是Exectuor,Executor是一个抽象类,它不能被直接使用。
    
    2、但是它提供的两个子类ThreadPoolExecutor和ProcessPoolExecutor却是非常有用
    
    3、我们可以将相应的tasks直接放入线程池/进程池,不需要维护Queue来操心死锁的问题,线程池/进程池会自动帮我们调度。

    2. Future

    1、Future你可以把它理解为一个在未来完成的操作,这是异步编程的基础,
    2、传统编程模式下比如我们操作queue.get的时候,在等待返回结果之前会产生阻塞,cpu不能让出来做其他事情
    3、而Future的引入帮助我们在等待的这段时间可以完成其他的操作。

    concurrent.futures.ThreadPoolExecutor 抓取网页

    import requests
    from concurrent.futures import ThreadPoolExecutor
    
    def fetch_request(url):
        result = requests.get(url)
        print(result.text)
    
    url_list = [
        'https://www.baidu.com',
        'https://www.google.com/',         #google页面会卡住,知道页面超时后这个进程才结束
        'http://dig.chouti.com/',          #chouti页面内容会直接返回,不会等待Google页面的返回
    ]
    
    pool = ThreadPoolExecutor(10)            # 创建一个线程池,最多开10个线程
    for url in url_list:
        pool.submit(fetch_request,url)       # 去线程池中获取一个线程,线程去执行fetch_request方法
    
    pool.shutdown(True)                      # 主线程自己关闭,让子线程自己拿任务执行
  • 相关阅读:
    《将博客搬至CSDN》
    java-FileUtils(复制文件夹、复制文件、字符串直接写入文件中)(新手)
    java-FileUtils(读取、判断、获取)-(新手)
    一.MySQL入门基础
    二.压缩指令的应用
    一.档案与目录管理
    四.mysql演示银行转账
    三.实例演示insert/update/delect更新数据库
    二.数据库游标对象cursor与实例
    一.数据库连接对象connection
  • 原文地址:https://www.cnblogs.com/xiaoxiamiaichiyu/p/14583740.html
Copyright © 2020-2023  润新知