• 102.多线程


    相关概念

    • 程序:写好的代码,保存在硬盘里
    • 进程:进行中的程序,在内存中运行,相当于把硬盘中的程序挪到内存中,并执行。
    • 线程:一个进程中独立运行的片段,同一个进程可以有多个线程,共享同一个进程的数据和上下文运行环境
    • 全局解释器锁:在python主循环中只能有一个控制线程在执行

    _thread包

    • 此包在python3中尽量不用
    • thread:有问题不用,python3中已经改成_thread
    • _thread.start_new_thread(函数名, 参数元组)
    #_thread 例子
    #问题1:主线程提前结束,导致分线程不一定能正常结束
    #问题2:分线程运行顺序不定
    import time
    import _thread as thr
    
    def loop1():
        print("loop1开始工作:", time.ctime())
        time.sleep(4)
        print("loop1结束工作:", time.ctime())
    
    def loop2():
        print("loop2开始工作:", time.ctime())
        time.sleep(2)
        print("loop2结束工作:", time.ctime())
    
    def main():
        print("主程序开始工作:", time.ctime())
        thr.start_new_thread(loop1, ())
        thr.start_new_thread(loop2, ())
        print("主程序结束工作:", time.ctime())
    
    if __name__ == "__main__":
        main()
    

    threading包

    • 在python3中,多数在用此包

    • threading.Thread(target=函数名, args=参数元组) 实例化线程

    • threading.Timer(3.0, 函数名,[参数]) 指定时间后执行函数

    • .start() 启动线程

    • .join() 等待子线程执行完毕

      #threading 例子
      import time
      import threadingr
      
      def loop1():
          print("loop1开始工作:", time.ctime())
          time.sleep(4)
          print("loop1结束工作:", time.ctime())
      
      def loop2():
          print("loop2开始工作:", time.ctime())
          time.sleep(2)
          print("loop2结束工作:", time.ctime())
      
      def main():
          print("主程序开始工作:", time.ctime())
          #第一个实例线程
          l1 = threading.Thread(target=loop1, args=())
          l1.start()
          #第二个实例线程
          l2 = threading.Thread(target=loop2, args=())
          l2.start()
          #主程序等待两个线程
          l1.join()
          l2.join()
      
          print("主程序结束工作:", time.ctime())
      
      if __name__ == "__main__":
          main()
      
    • .setDaemon(True)或.deamon=True 守护线程

      • 守护线程会在主线程结束时一起结束(和运行环境有关,不一定实现)
      • 在启动线程前进行设置
      #线程守护例子
      import time
      import threading
      
      def loop1():
          print("子线程开始")
          time.sleep(2)
          print("子线程结束")
      
      def main():
      
          print("主线程开始")
      
          l = threading.Thread(target=loop1, args=())
          l.setDaemon(True)
          l.start()
      
          print("主线程结束")
      
      if __name__ == "__main__":
          main()
      
    • threading常用属性

      • threading.currentThread() : 返回当前运行中线程变量(整体线程元素)
      • threading.enumerate() : 返回当前活动线程的数量,已列表形式
      • threading.activeCount() : 返回当前活动线程的数量,相当于len(threading.enumerate)
      • threading.setName() : 给线程设置名字
      • threading.getName() : 返回线程的名字
      import time
      import threading
      
      def loop1():
          print("线程1开始")
          time.sleep(2)
          print("线程1结束")
      
      def loop2():
          print("线程2开始")
          time.sleep(4)
          print("线程2结束")
      
      def loop3():
          print("线程3开始")
          time.sleep(6)
          print("线程3结束")
      
      def main():
      
          print("主线程开始")
      
          l1 = threading.Thread(target=loop1, args=())
          l1.setName("l1")
          l1.start()
      
          l2 = threading.Thread(target=loop2, args=())
          l2.setName("l2")
          l2.start()
      
          l3 = threading.Thread(target=loop3, args=())
          l3.setName("l3")
          l3.start()
      
          time.sleep(1)
      
          print("当前运行线程的参数的变量名:", threading.currentThread())
          print("当前活动线程数量:", threading.activeCount())
       
          for i in threading.enumerate():
              print(i.getName())
      
          print("主线程结束")
      
      if __name__ == "__main__":
          main()
      

    另一种方式

    • threading.Thread子类方式

    • 子类可不写__init__函数,但是如果写,就一定要调用父类的__init__函数

    • 子类必须重新构run函数,run函数是执行内容,就是target参数所给函数的内容 .start()所要执行的内容

      import time
      import threading
      
      class T(threading.Thread):
      
          def __init__(self, arg):
              self.arg = arg
              #python2中用super(T, self).__init__()
              super().__init__()
        
          def run(self):
              time.sleep(2)
              print(self.arg)
      
      for i in range(5):
      
          l = T(i)
          l.start()
          l.join()
      
      print("主线程结束")
      

    共享变量

    多个线程同时操作一个变量

    • 由于操作顺序不定,导致结果不同
    • 解决方法:threading.lock()
    import threading
    import time
    
    s = 0
    ls = 100000
    
    tlock = threading.Lock()
    
    def jia():
        #global把局部变量变为全局变量
        global s, ls
        time.sleep(1)
        for i in range(1, ls):
            tlock.acquire()
            s += 1
            tlock.release()
    
    def jian():
      
        global s, ls
        time.sleep(1)
        for i in range(1, ls):
            tlock.acquire()
            s -= 1
            tlock.release()
    
    if __name__ == "__main__":
      
        j1 = threading.Thread(target=jia, args=())
        j2 = threading.Thread(target=jian, args=())
    
        j1.start()
        j2.start()
    
        j1.join()
        j2.join()
    
        print(s)
    

    死锁问题

    • 解决办法threading.acquire(timeout=4) 超过四秒就不在申请

      • 若申请不到,就不能释放,此时做if处理
    • treading.semphore(3) 同时允许3个进程使用某变量

      import threading
      import time
      
      sem = threading.Semaphore(3)
      
      def func():
      
          if sem.acquire():      
           
              print(threading.currentThread().getName() + "获得线程锁")        
              time.sleep(10)
              sem.release()
              print(threading.currentThread().getName() + "释放线程所")
      
      for i in range(8):
          t = threading.Thread(target=func, args=())
          t.start()
      

    可重入锁问题

    • 同一个线程多次申请锁的问题

    • 一般用于递归函数

    • threading.RLock()

      import threading
      import time
      
      class xiancheng(threading.Thread):
          def run(self):
              global a
              time.sleep(1)
              if loc.acquire(timeout=1):
                  a = a + 1
                  s = self.name + "设置数" + str(a)
                  print(s)
                  loc.acquire()
                  loc.release()
                  loc.release()
      
      def tx():
          for i in range(5):
              l = xiancheng()
              l.start()
      
      if __name__ == "__main__":
          a = 0
          loc = threading.RLock()#实例可重入锁
          tx()
      

    变量安全

    • 不安全变量类型:list, set, dict等,可改变的变量类型
    • 安全变量:queue队列
      • queue.put(数据) 存入数据
      • queue.get() 去除数据
    import queue
    import time
    import threading
    
    class shengchanzhe(threading.Thread):
        def run(self):
            global q
            a = 0
            while True:
                if a < 1000:
                    for i in range(100):
                        a = a + 1
                        b = "产品:" + str(a)
                        q.put(b)
                        print(b)
                time.sleep(0.5)
    
    class xiaofeizhe(threading.Thread):
        def run(self):
            global q
            while True:
                if q.qsize() > 100:
                    for i in range(5):
                        #线程有个.name属性,即线程名,可用.setName()和.getName()修改
                        b = self.name + "消费了" + q.get()
                        print(b)
                time.sleep(1)
    
          
    
    if __name__ == "__main__":
    
        q = queue.Queue()
      
        for i in range(3):
            s = shengchanzhe()
            s.start()
      
        for i in range(5):
            x = xiaofeizhe()
            x.start()
    

    线程的替代方案

    • subprocess
      • 完全跳过线程,使用进程
      • 派生进程的主要替代方式
      • python2.4后引入
    • multiprocessiong
      • 使用threading接口派生,使用子进程
      • 允许多核或多cpu派生进程,接口和threading极其相似
      • python2.6后引入
    • concurrent.futures
      • 新的异步执行模块
      • 任务级别的操作
      • python3.2后引入
  • 相关阅读:
    Cypress自动化框架笔记
    SSH 用公钥免密登录,需要改文件权限
    python函数的可变参数 *和**
    Socket进程处理被中断的系统调用及Accept函数返回EINTR错误处理 (转)
    perror 与 strerror
    perror 函数
    strerror 函数
    getch ()函数 (来自百度百科)
    C语言运算符及其优先级汇总表口诀
    第四章
  • 原文地址:https://www.cnblogs.com/TK-tank/p/12311444.html
Copyright © 2020-2023  润新知