• Python多线程教程



    多线程介绍

    什么是线程

    ​ 线程(Thread)也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。

    ​ 个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。

    为什么需要多线程

    • 进程之间不能共享内存,但线程之间共享内存非常容易。
    • 使用多线程来实现多任务并发执行比使用多进程的效率高。
    • 多个线程需要并发处理,并共享处理结果。

    多线程实现方法

    创建线程

    1.普通创建线程方法
    • 调用threading模块:

      import threading,time
      
      def run(input):
          for i in range(10):
              print('sub task:%s'%input)
              time.sleep(1)
      
      if __name__ == '__main__':
          t = threading.Thread(target=run, args=("hello world",))
          t.start()
          for i in range(10):
              print('main task')
              time.sleep(1)    
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    2.Thread类继承方法
    • 新建类,继承自threading.Thread类:

      import threading
      import time
      
      class MyThread(threading.Thread):
          def __init__(self, input):
              super(MyThread, self).__init__()  # 重构run函数必须要写
              self.input = input
      
          def run(self):
            	for i in range(10):
                  print("task", self.input)
                  time.sleep(1)
      
      if __name__ == "__main__":
          t1 = MyThread("hello world 1")
          t2 = MyThread("hello world 2")
          t1.start()
          t2.start()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

    守护线程

    1.设置子线程为守护线程
    • 使用setDaemon(True)把所有的子线程都变成了主线程的守护线程,因此当主进程结束后,子线程也会随之结束

      import threading
      import time
      
      def run(n):
          for i in range(10):
              print("task", self.input)
              time.sleep(1)
      
      if __name__ == '__main__':
          t = threading.Thread(target=run, args=("hello world",))
          t.setDaemon(True)   # 主线程kill,子线程自动kill
          t.start()
          print("end")
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    2.主线程等待子线程结束后执行下一步
    • 为了让守护线程执行结束之后,主线程再结束,我们可以使用join方法,让主线程等待子线程执行。

      import threading
      import time
      
      def run(n):
          for i in range(10):
              print('sub task')
              time.sleep(1)
      
      if __name__ == '__main__':
          t = threading.Thread(target=run, args=("t1",))
          t.setDaemon(True)   # 主线程kill,子线程自动kill
          t.start()
          t.join()  # 主线程停止等待子线程执行完毕后,再继续执行!
                    # 注释掉之后,就是子线程、主线程同时跑!
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    3.多线程共享全局变量
    • 线程是进程的执行单元,进程是系统分配资源的最小单位,所以在同一个进程中的多线程是共享资源的。

      import threading
      import time
      g_num = 100
      
      def run(n):
          global g_num
          for i in range(10):
              print('sub task')
              g_num += 1
              time.sleep(1)
      
      if __name__ == '__main__':
          t = threading.Thread(target=run, args=("t1",))
          t.setDaemon(True)   # 主线程kill,子线程自动kill
          t.start()
          # t.join()  # 主线程停止等待子线程执行完毕后,再继续执行!
                      # 注释掉之后,就是子线程、主线程同时跑!
          for i in range(10):
              print('main task:%d'%g_num)
              time.sleep(1)    
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    4.互斥锁
    • 多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁,即同一时刻允许一个线程执行操作。

    • 如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期,我们也称此为**“线程不安全”**。

    • 线程锁用于锁定资源,你可以定义多个锁, 像下面的代码, 当你需要独占某一资源时,任何一个锁都可以锁这个资源,就好比你用不同的锁都可以把相同的一个门锁住是一个道理。

      from threading import Thread,Lock
      import os,time,threading
      reg = 0
      
      def work1():
          global reg
      
          lock.acquire()
          for i in range(10):
              reg = i
              print(reg)
              time.sleep(0.5)
          lock.release()
      
      def work2():
          global reg
      
          lock.acquire()
          for i in range(10):
              reg = i * 10
              print(reg)
              time.sleep(0.5)
          lock.release()
      
      
      if __name__ == '__main__':
          lock=Lock()
          t1 = threading.Thread(target=work1)
          t2 = threading.Thread(target=work2)
          t1.start()
          t2.start()
          t1.join() # hold on ,wait for this thread
          t2.join() # hold on ,wait for this thread
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
    5.递归锁
    • RLcok类的用法和Lock类一模一样,但它支持嵌套,在多个锁没有释放的时候一般会使用RLcok类。

      import threading
      import time
      
      def Func(lock):
          global gl_num
          lock.acquire()
          gl_num += 1
          time.sleep(1)
          print(gl_num)
          lock.release()
      
      if __name__ == '__main__':
          gl_num = 0
          lock = threading.RLock()
          for i in range(10):
              t = threading.Thread(target=Func, args=(lock,))
              t.start()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    6.信号量
    • 互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。

      import threading
      import time
      
      def run(n, semaphore):
          semaphore.acquire()   #加锁
          time.sleep(1)
          print("run the thread:%s\n" % n)
          semaphore.release()     #释放
      
      if __name__ == '__main__':
          num = 0
          semaphore = threading.BoundedSemaphore(5)  # 最多允许5个线程同时运行
          for i in range(22):
              t = threading.Thread(target=run, args=("t-%s" % i, semaphore))
              t.start()
          while threading.active_count() != 1:
              pass  # print threading.active_count()
          else:
              print('-----all threads done-----')
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
    7.事件(Event类)
    • python线程的事件用于主线程控制其他线程的执行,事件是一个简单的线程同步对象,其主要提供以下几个方法:

      • clear 将flag设置为“False”
      • set 将flag设置为“True”
      • is_set 判断是否设置了flag
      • wait 会一直监听flag,如果没有检测到flag就一直处于阻塞状态
    • 事件处理的机制:全局定义了一个“Flag”,当flag值为“False”,那么event.wait()就会阻塞,当flag值为“True”,那么event.wait()便不再阻塞。

      #利用Event类模拟红绿灯
      import threading
      import time
      
      event = threading.Event()
      
      # 前5秒是绿灯,5~10秒是红灯。
      def lighter():
          count = 0
          event.set()     #初始值为绿灯
          while True:
              if 5 < count <=10 :
                  event.clear()  # 红灯,清除标志位
                  print("\33[41;1mred light is on...\033[0m")
              elif count > 10:
                  event.set()  # 绿灯,设置标志位
                  count = 0
              else:
                  print("\33[42;1mgreen light is on...\033[0m")
      
              time.sleep(1)
              count += 1
      
      def car(name):
          while True:
              if event.is_set():      #判断是否设置了标志位
                  print("[%s] running..."%name)
                  time.sleep(1)
              else:
                  print("[%s] sees red light,waiting..."%name)
                  event.wait()
                  print("[%s] green light is on,start going..."%name)
      
      light = threading.Thread(target=lighter,)
      light.start()
      
      car = threading.Thread(target=car,args=("MINI",))
      car.start()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38

    Python多线程内在逻

    • 在非python环境中,单核情况下,同时只能有一个任务执行。多核时可以支持多个线程同时执行**。但是在python中,无论有多少核,同时只能执行一个线程。**
    • GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。
    • Python多线程的工作过程:(python在使用多线程的时候,调用的是c语言的原生线程)
      • 拿到公共数据
      • 申请gil
      • python解释器调用os原生线程
      • os操作cpu执行运算
      • 当该线程执行时间到后,无论运算是否已经执行完,gil都被要求释放
      • 进而由其他进程重复上面的过程
      • 等其他进程执行完后,又会切换到之前的线程(从他记录的上下文继续执行),整个过程是每个线程执行自己的运算,当执行时间到就进行切换(context switch)。

    参考教程

    https://www.cnblogs.com/luyuze95/p/11289143.html

    个人博客地址

    https://weicun.gitee.io/

  • 相关阅读:
    【编程教室】PONG - 100行代码写一个弹球游戏
    AIR32F103(三) Linux环境基于标准外设库的项目模板
    队列的实现(c语言)
    GaussDB数据库SQL系列-聚合函数
    基于Java所涉及的人工智能的框架
    点赋科技:建设智能饮品高地,打造数字化产业先锋
    mac 安装使用svn教程
    互联网大厂的测试员是怎么交付测试项目文档的
    JDK17 New Feature
    java基本语法 下
  • 原文地址:https://blog.csdn.net/Jimmy_wei_2010/article/details/127637239