python多线程编程(基础)

下面是多线程编程基础教程

python多线程编程

  • 线程与进程

    1. 进程:进程就是执行中的应用程序,进程可派生新的进程来执行其他任务,各个进程通过进程间通信(IPC)进行信息共享

    2. 线程:与进程类似,但是线程实在同一个进程下执行的,并共享相通的上下文,就是类似在一个主进程下运行了多个迷你进程


      线程包括开始,执行顺序,结束三部分组成,通过指令指针记录当前的上下文,当其他线程运行时,可以中断或睡眠,此种模式称为让步


      一个进程中的各个线程与主线程共享同一片数据空间,线程一般通过并发方式执行


      多个线程同时访问一块数据空间时,由于访问顺序不同会导致不同的结果,此种现象称为竞态条件


      注意线程无法给予公平的执行时间


  • 全局解释器锁

    python代码由python的虚拟机控制,在解释器主循环中同时只能有一个控制线程执行,虽然内存中有许多程序,但在任意给定时刻只能有一个程序在运行,即在任意时刻只有一个线程被python解释器来执行


    python虚拟机的访问由全局解释器锁(GIL)来控制,锁保证了只能有一个线程在运行


    在多线程环境下,python解释器按照下列方式执行:

    1. 设置GIL
    2. 切换进一个线程中执行
    3. 执行下面的操作之一:
      • 指定数量的字节码指令
      • 线程主动让出控制权
    4. 把线程设置回睡眠状态(切换出线程)
    5. 解锁GIL
    6. 重复上述步骤

  • 退出线程

    当一个线程完成函数的执行时,就会退出,可以通过调用thread.exit()退出函数,或sys.exit()退出python的进程,还可以抛出异常


  • 在pythn中使用线程

    下面是一个单线程执行循环

    from time import sleep,ctime
    
    def loop1():
        print("loop1 Starting at {}".format(ctime()))
        sleep(4)
        print("loop1 Ending at {}".format(ctime()))
    
    def loop2():
        print("loop2 Starting at {}".format(ctime()))
        sleep(2)
        print("loop2 Ending at {}".format(ctime()))
    
    def main():
        print("Main starting at {}".format(ctime()))
        loop1()
        loop2()
        print("Main ending at {}".format(ctime()))
    
    if __name__ == '__main__':
        main()
    '''显示结果如下:
    Main starting at Fri May 22 08:21:08 2020
    loop1 Starting at Fri May 22 08:21:08 2020
    loop1 Ending at Fri May 22 08:21:12 2020
    loop2 Starting at Fri May 22 08:21:12 2020
    loop2 Ending at Fri May 22 08:21:14 2020
    Main ending at Fri May 22 08:21:14 2020'''
    
    
    • 有程序的执行看出用了6秒钟执行完

  • python中的threading模块

    • threading模块的对象

      对象 描述
      Thread 表示一个执行线程的对象
      Lock 锁原语对象
      RLock 可重入锁对象,使单一线程可以再次获得已持有的锁(递归锁)
      Condition 条件变量对象,使得一个线程等待另一个线程满足特定条件
      Event 条件变量的通用版本,任意数量的线程等待某个事件发生,在事件发生后,所有的线程被激活
      Semaphore 为线程间共享的有限资源提供一个计数器,若没有则会被阻塞
      BoundedSemaphore 与Semaphore类似,但不允许超过初始值
      Timer 与Thread类似,不过他要在运行前等待一段时间
      Barrier 创建一个障碍,必须达到指定的数量的线程后才可以继续
    • python的threading模块支持守护线程,守护线程可以作为服务器线程,当主线程退出时,将其他的线程设置为守护线程时,程序不必在意守护线程是否终止

    • 守护线程工作方式是:等待客户端请求若没有请求,守护线程为空闲状态

    • 当主线程退出时,若不需要等待某下子线程的结束,可以子线程设置守护进程标记,thread.daemon=True

    • 注意新的子线程会继承父线程的守护标记

    • 整个python程序会在非守护线程结束后才终止程序的执行

  • threading的Thread类

    属性 描述
    Thread对象数据属性 ******
    name 线程名
    ident 线程的标识符
    daemon 布尔标志,表示此线程是否为守护线程
    Thread对象方法 ******
    init(group=None,target=None,name=None,args=(),kwargs={},verbose=None,daemon=None) 实例化一个线程对象需要有一个可调用的target,以及其他参数args或kwargs,还可以传递name或group参数,verbose标志可以接收,而daemon的值会设定为thread.daemon属性/标志
    start() 开始执行该线程
    run() 定义线程功能的方法
    join(timeout=None) 直至启动的线程终止前一直挂起,除非给出timeout,否则一直阻塞
    getName() 返回线程名
    setName(name) 设定线程名
    isAlivel/is_alive() 布尔标志,表示线程是否还存活
    isDaemon() 判断是否为守护线程
    setDaemon(daemonic) 把线程的守护标志设定为布尔值daemonic(需要在线程start()之前调用)
    • 需要注意set/getName方法已弃用,使用thread.name来获取或设定
    • is/setDaemon已弃用,使用thread.daemon来获取或设定

  • 创建线程方法

    • 创建Thread的实例,传给他一个函数
    • 创建Thread的实例,传给他一个可调用的实例(不做介绍)
    • 派生Thread的子类,并创建子类的实例

  • 创建Thread实例,传函数

    import threading
    from time import ctime,sleep
    
    loopstime=[4,2]  #定义每个线程需要等待的时间
    
    def loop(nloop,nsec):  #定义循环程序
        print("loop {} started at {}".format(nloop,ctime()))
        sleep(nsec)
        print("loop {} ended at {}".format(nloop,ctime()))
    
    def main():  
        print("Main started at {}".format(ctime()))
        threads=[]  #将实例化的对象存储在列表中,方便以后调用
        nloops=range(len(loopstime))  #需要实例化线程的次数
        for i in nloops:
            t=threading.Thread(target=loop,args=(i,loopstime[i]))  #实例化线程对象
            threads.append(t)  #将创建的对象暂时存储在列表中
    
        for i in nloops:
            threads[i].start()  #开始执行线程
    
        for i in nloops:
            threads[i].join()  #等待所有线程执行完毕
    
        print("All Done at {}".format(ctime()))
    
    if __name__=='__main__':
        main()
    
    
    • 实例化每个Thread对象时,把函数(target)和参数(args)传进去,得到返回的Thead类实例对象
    • 当所有的线程分配完成后,通过调用每个线程的start()方法执行线程,而不是在创建时就执行
    • 注意对于join()函数可以不用调用,一旦线程start(),他们会一直执行,直到给定函数完成后退出,若主线程还有其余的事情时,不是等待线程完成,可以不调用join函数

  • 派生Thread的子类,并创建子类的实例

    from time import sleep,ctime
    import threading
    
    loops=(4,2)
    
    class MyThread(threading.Thread):
        def __init__(self,func,args,name=''):
            threading.Thread.__init__(self)
            self.func=func
            self.args=args
            self.name=name
    
        def run(self):
            return self.func(*self.args)
    
    def loop(nloop,nsec):
        print("loop {} started at {} ".format(nloop,ctime()))
        sleep(nsec)
        print("loop {} started at {} ".format(nloop,ctime()))
    
    def main():
        print("Main started at {}".format(ctime()))
        threads=[]
        count=range(len(loops))
    
        for i in count:
            t=MyThread(loop,(i,loops[i]),loop.__name__)
            threads.append(t)
    
        for i in count:
            threads[i].start()
    
        for i in count:
            threads[i].join()
    
        print("All done at {}".format(ctime()))
    
    if __name__ == '__main__':
        main()
    
    
    '''显示结果如下:
    Main started at Fri May 22 15:21:12 2020
    loop 0 started at Fri May 22 15:21:12 2020 
    loop 1 started at Fri May 22 15:21:12 2020 
    loop 1 started at Fri May 22 15:21:14 2020 
    loop 0 started at Fri May 22 15:21:16 2020 
    All done at Fri May 22 15:21:16 2020 '''
    
    

  • threading模块的其他函数

    函数 描述
    activeCount/active_count() 当前活动的Thread对象个数
    current Thread()/current_thread 返回当前的Thread对象
    enumerate() 返回当前活动的Thread对象列表
    setttace(func) 为所有线程设置一个trace函数
    setprofile(func) 为所有线程设置一个profile函数
    stack_size(size=0) 返回新创建线程的栈大小,或为后续创建的线程设定栈的大小为size

  • 单线程与多线程的比较

    from e import MyThread
    from time import sleep,ctime
    
    def fib(x):  #斐波那契
        sleep(0.3)
        if x < 2:
            return 1
        return (fib(x-1) + fib(x-2))
    
    def fac(x):   #阶乘
        sleep(0.4)
        if x < 2:
            return 1
        return (x * fac(x-1))
    
    def sum(x):  #累加和
        sleep(0.5)
        if x < 2:
            return 1
        return (x + sum(x-1))
    
    def main():
        funcs=[fib,fac,sum]
        n=12
        threads=[]
        count=range(len(funcs))
    
        #Sigle Thread executed
        for i in count:
            print("{} Starting at {} ".format(funcs[i].__name__,ctime()))
            funcs[i](n)
            print("{} ended  at {} ".format(funcs[i].__name__, ctime()))
    #————————————上述为单线程,下面为多线程——————————————————————
        print("\n")
        print("MultiThread executed")
        def loop(nloop,nsec):
            print("loop {} started at {}".format(nloop,ctime()))
            sleep(nsec)
            print("loop {} ended at {}".format((nloop,ctime())))
    
        for i in count:
            t=MyThread(funcs[i],(n,),funcs[i].__name__)
            threads.append(t)
    
        for i in count:
            threads[i].start()
    
        for i in count:
            threads[i].join()
            print(threads[i].getResult())
    
        print("All done!!!")
    
    if __name__ == '__main__':
        main()
    
    '''显示结果如下:
    单线程(用时2分30秒):
    fib Starting at Fri May 22 15:55:00 2020 
    fib ended  at Fri May 22 15:57:19 2020 
    fac Starting at Fri May 22 15:57:19 2020 
    fac ended  at Fri May 22 15:57:24 2020 
    sum Starting at Fri May 22 15:57:24 2020 
    sum ended  at Fri May 22 15:57:30 2020 
    
    多线程(用时2分20秒):
    MultiThread executed 
    func fib started at Fri May 22 15:57:30 2020
    func fac started at Fri May 22 15:57:30 2020
    func sum started at Fri May 22 15:57:30 2020
    func fac ended at Fri May 22 15:57:35 2020
    func sum ended at Fri May 22 15:57:36 2020
    func fib ended at Fri May 22 15:59:50 2020
    233
    479001600
    78
    All done!!!'''
    
    • 上述单线程就是顺序执行三个函数
    • 多线程比单线程快了10秒

上述是基本的多线程基础编程,希望对各位有所帮助!

你可能感兴趣的:(python,笔记,python,多线程)