zoukankan      html  css  js  c++  java
  • python的线程

    并发:
    同时做某些事情,互不干扰的同一时刻做几件事
     
    并行:
    在同时间段,同时执行或处理多个请求
     
    解决高并发:
    1、队列,缓冲区,设置优先队列
    2、争抢,锁机制
    3、预处理,缓存常用
    4、并行,水平扩展,多开进程,进程实现并行处理
    5、提速,垂直提升硬件
     
    进程和线程
     
    在实现了线程的操作系统中,线程是操作系统能够进行运算调度的最小单位,他没包含在进程中,是进程中的实际运作单位,一个程序的执行实例,就是一个进程
     
    继承,是计算机中的程序关于某数据集合上的一次运行活动,是操作系统结构的基础
     
    进程和程序的关系
     
    linux有父进程和子进程,windows是平等的关系
     
    线程,有时被称为轻量级进程,是程序执行流的最小单元
     
    进程和线程的理解:
    每一个线程都认为自己独占所有的计算机资源
    进程是独立的空间,进程间的资源是可以供线程调度使用的
    线程就是省份,每一个线程拥有自独立的堆栈
     
    线程的状态
     
    状态
    含义
    就绪
    线程能够运行,但在等到被调度
    运行
    线程正在运行
    阻塞
    线程等待外部时间发生而无法运行,如I/O操作
    终止
    线程完成,或退出,或被取消
     
    python中的进程和线程:
    进程会启动一个解释器进程,线程共享一个解释器进程A
     
    python的线程开发
    python的线程使用标准库threading
     
    Thread类
    def __init__(self,group = None,target = None,name = None,args = () ,kwargs = None,*,daemon = None)
     
    参数名
    含义
    target
    线程调用的对象,就是目标函数
    name
    为线程起个名字
    args
    为目标函数传递实参,元组
    kwargs
    为目标函数关键字传参,字典
     
    线程启动
    import threading
    def worker():
        print("im working")
        print("Fineshed")
     
    t = threading.Thread(target=worker,name="s1")   #定义线程
    t.start() #线程启动
     
    通过threading.Thread创建一个线程对象,target是目标函数,name可以指定名称
    线程的启动必须要有start方法
    线程之所以执行函数,是因为线程中就是执行代码的,而最简单的封装就是,所以还是函数调用
    函数执行完,线程就会退出了
     
    线程退出
    python没有提供线程退出的方法,但是线程可以在下面的情况退出
    1、线程函数内语句执行完毕
    2、线程函数中抛出未处理的异常
    import threading
    import time
     
    def worker():
        #count =0
        while True:
            if count > 5:
                raise RuntimeError(count)   #抛出异常,线程中断
            time.sleep(1)
            print('im working')
            count +=1
     
    t = threading.Thread(target=worker,name="s1")   #定义线程
    t.start() #线程启动
    print('==End==')
    python中的线程没有优先级,没有线程组的概念,也不能被销毁,停止,挂起,自然也就没有恢复,中断了
     
    线程的传参
    线程的传参和函数传参没有什么区别,本质上就是函数传参
     
    threading的属性和方法
     
    名称
    含义
    current_thread()
    返回当前线程对象
    main_thread()
    返回主线程对象
    active_couny()
    当前处于alive状态的线程个数
    enumerate()
    返回当前所有活着的线程的列表,不包括已经终止的线程和未开始的的线程
    get_ident()
    返回当前线程的ID,非0整数
    active_count(),enumerate() 方法返回值还包括主线程
     
    Thread实例的属性和方法
     
    名称
    含义
    name
    只是一个名字,名称可以重名,getName(),setName()获取,设置这个名词
    ident
    线程ID,他是非0整数,线程启动后才有ID,否则为None,,线程退出,此ID依旧可以访问,此ID可以重复使用
    is_alive
    返回线程是否活着
    线程的name这是一个名称,可以重复,ID必须唯一,但可以在线程退出后在利用
     
    import threading
    import time
    def worker():
        count= 0
        while True:
            if count > 5:
                break
            time.sleep(1)
            count+=1
            print(threading.current_thread().name)
    t = threading.Thread(name='worker',target=worker)
    print(t.ident)
    t.start()
     
    while True:
        time.sleep(1)
        if t.is_alive():
            print('{} {}'.format(t.name,t.ident))
        else:
            print('{} {}'.format(t.name,t.ident))
        #t.start()  同一线程在没有退出前,只能start一次
     
     
    名称
    含义
    start()
    启动线程,每一个线程必须且只能执行一次
    run()
    运行线程函数
     
    start()方法会调用run()方法,而run()可以运行函数
    start()是启动一个函数,而run是执行线程内的函数,两者没有可比性
    使用start方法才能启动多个线程
     
    多个线程
    顾名思义,多个线程,一个进程中如果有多个线程,就是多线程,实现一种并发
     
    当使用start方法启动线程后,进程内有多个活动的线程并行的工作,就是多线程
    其他线程成为工作线程
     
     
    线程的安全、
    print函数在多线程内是不安全的,
    线程安全:线程执行一段代码,不会产生不确定的结果,那这段代码就是线程安全的
     
    1、如何保证print打印完全
    字符串是不可变类型的,它可以作为一个整体不被分割输出,end=' ' 就可以不再让print输出换行了
     
    2、使用logging
    标准库中的logging模块,日志处理模块,线程安全的,生成环境代码都是用logging
     
     
    daemon线程,和non-daemon线程
    进程靠线程执行代码,至少有一个主线程,其他线程都是工作线程
     
    主线程是第一个启动的线程
    父线程:如果线程A中启动了一个线程B,A就是B的父线程
    子线程:B就是A的字线程
     
    在python中,构造线程的时候,可以设置daemon属性,这个属性,必须在start方法前设置好
    源码Thread的__init__中,
    if daemon is not None:
         self._daemonic = daemon
    else:
         self._daemonic = current_thread().daemon     #若没有声明daemon的值,则会向最近的线程继承daemon值
    线程daemon属性,如果设定就是好用户的设置,否则就取当前线程的daemon值
    主线程是non-daemon线程,即daemon = False
    import threading
    import time
    def foo():
        time.sleep(5)
        for i in range (20):
            print(i)
     
    t = threading.Thread(target=foo,daemon=True)
    t.start()
    print('~~~~~~~')
     
    总结
    线程具有一个daemon属性,可以显示设置为True或者False,也可以不设置,去默认值None
    如果不设置daemon,就取当前线程的daemon来设置它
    主线程是non-daemon,即daemon = False
    从主线程创建的所有线程的不设置daemon属性,则默认都是daemon=False,也就是non-daemon线程
     
    python程序在没有活着的non-daemon线程运行时退出,也就是剩下的只能是daemon线程,主线程才能退出,否则主线程只能等待
    import threading
    import time
    def foo():
        for i in range(20):
            print(i)
        t = threading.Thread(target=bar,daemon=True)#True不执行,Fasle主线程等待
        t.start()
     
    def bar():
        time.sleep(5)
        print('bar')
     
    t = threading.Thread(target=foo,daemon=False)   #主线程若为True,则不会执行工作线程
    t.start()
    print('Main Thread Exiting')
     
    如果有 non- daemon的时候,主线程退出,不会杀掉所有daemon线程,知道所有non-daemon线程全部结束,如果还有daemon线程,主线程需要退出,会结束所有daemon线程,退出
     
     
    join方法
     
    import threading
    import time
    def foo():
        for i in range(20):
            print(i)
        time.sleep(2)
        print('~~~~~~~~~~~')
     
    t = threading.Thread(target=foo,daemon=True)#True不执行,Fasle主线程等待
    t.start()
    t.join()    #主线程join了t线程中,会一直等待t这个线程执行完毕后,退出线程
     
    print('Main Thread Exiting')
     
    使用join方法后,daemon线程执行完了,主线程才退出了
    join(time = None),是线程的标准方法之一
     
    一个人线程中调用另一个线程的join方法,调用者将被阻塞,就一直等到被调用结束为止
    一个线程可以被join多次
    timeout参数指定调用者等待多久,没有设置超时,就一直等到被调用线程结束
    谁调用谁的 join方法,就是join谁,就要等谁
     
    daemon线程的应用场景
    本来并没有daemon thread,为了简化程序员的工作,让他们不用去记录和管理那些后台线程,,创造力一个人daemon thread的概念,这个概念唯一的作用就是,当你把一个线程设置为daemon它会随主线程的退出而退出
     
    主要应用场景有:
    1、后台任务,(发送心跳包,监控,这种场景应用颇广
    2、主线程工作才有用的线程,如主线程中维护这公共的资源,主线程已经清理例了,准备退出,而工作线程使用这些资源工作也没有意义了,一起退出最合适
    3、随时可以被终止的线程
     
    如果主线程退出,想所有其他工作线程一起退出,就是用daemon-True来创建工作线程
    daemon线程,简化了程序员手动关闭线程的工作
     
    如果在non-daemon中线程A中,对另一个daemon线程B使用了join方法,这个线程B设置成daemon就没有什么意义了,因为non-daemon线程A总是要等线程B
    如果在一个daemeon中C中,对另一个线程D使用了join方法,只能说明C要等D,主线程退出,C和D不管是否结束,也不管他们等谁,都要被杀掉
     
    举例:
    import threading
    import time
     
    def bar():
        while True:
            time.sleep(1)
            print('bar')
     
    def foo():
        print('t1 daemon = {}',format(threading.current_thread().isDaemon()))
        t2 = threading.Thread(target=bar)
        t2.start()
        print('t2 daemon = {}',format(t2.isDaemon()))
        t2.join()
     
     
    t1 = threading.Thread(target=foo,daemon=True)#True不执行,Fasle主线程等待
    t1.start()
    #t1.join()    #若主线程join了t线程中,会一直等待t这个线程执行完毕后,退出线程
    time.sleep(3)
    print('Main Thread Exiting')
     
    threading.local类
     
    python提供threading.local类,将这个类实例化得到一个全局对象,但是不同的线程使用这个对象存储的数据其他线程看不见
    import threading
    import time
     
    global_data = threading.local()
     
    def woker():
        global_data.x = 0
        for i in range(100):
            time.sleep(0.001)
            global_data.x +=1
        print(threading.current_thread(),global_data.x)
     
    for i in range(10):
        threading.Thread(target=woker).start()
    结果显示和使用局部变量的效果一样
     
    import threading
    import time
    X = 'abc'
    ctx = threading.local()
    ctx.x = 123
     
     
    def worker():
        print(X)
        print(ctx)
        # print(ctx.x)
        print('working')
     
    worker()
    print()
    threading.Thread(target=worker).start()
     
    从运行结果来看,另一个线程ctx.x出错了
     
    但是ctx没有错,说明看到ctx但是ctx中的x看不到,这个x不能跨线程
     
    threading.local 类构建了一个大字典,其元素是没一线程实例的地址为key和线程对象引用线程单独的字典的映射,如下:
    {id(Thread)->(ref(Thread),thread-local dict)}
    通过threading.local实例就可在不同的线程中,安全地使用线程独有的数据,做到了线程间数据隔离,如同本地变量一样安全
     
    定时器Timer/延迟执行
    threading.Timer继承自Thread ,这个类用来定义多久执行一个函数
    class threading.Timer(interval,function,args = None,kwargs = None)
    start方法执行之后,Timer对象会处于等待状态,等待了interval之后,开始执行function
    如果在执行函数之前的等待阶段,使用了cancel方法,就会跳过执行函数结束
     
    如果线程中worker函数已经开始执行,cancel就没有任何效果了
     
    总结,
    Timer是线程Thread的子类,就是线程类,具有线程的能力和特征
    它的实例是能够延时执行函数的线程,在真正执行目标函数之前,都可以cancel它
     
     
  • 相关阅读:
    Google Protocol Buffer 的使用和原理(转)
    在python开发工具PyCharm中搭建QtPy环境(详细)
    Docker容器的操作
    Docker镜像操作
    最新版本Docker的安装和使用
    linux CentOS如何安装KVM
    在Linux CentOS下如何安装tar.gz和RPM软件包
    Linux忘记root密码后如何在grub界面中以单用户模式进入系统并重置密码的方法
    Django中的Project和App的区别
    Python处理PDF和Word文档常用的方法(二)
  • 原文地址:https://www.cnblogs.com/spidermansam/p/8043645.html
Copyright © 2011-2022 走看看