首页 > 技术文章 > 多线程

W-Y-C 2019-07-24 16:11 原文

多线程

1线程理论知识

  • 什么是线程
    • 线程就是一条流水线
    • 进程 : 划分空间,加载资源,静态的.
    • 线程 : 执行代码,执行能力,动态的
  • 线程VS进程
    • 开启多进程开销非常大,开启线程开销非常小
    • 开启多进程的速度慢,开启多线程速度快
    • 进程之间数据不能直接共享,通过队列可以,同一进程下的线程之间的数据可以共享
  • 多线程的应用场景介绍
    • 并发:一个cpu来回切换,多进程并发,多线程的并发
    • 多进程并发:开启多个进程,每个进程里面的主线程执行任务
    • 多线程并发:开启一个进程,进程里面的多个线程执行任务

2.开启线程的两种方式

  • 第一种方式

    • # 第一种方式
      # from threading import Thread
      #
      # def task(name):
      #     print(f'{name} is running')
      #
      # if __name__ == '__main__':
      #     t = Thread(target=task,args=('mcsaoQ',))
      #     t.start()
      #
      #     print('主线程')
      
  • 第二种方式

    • from threading import Thread
      
      
      class MyThread(Thread):
      
      
          def run(self):
              print(f'{self.name} is running')
      
      
      if __name__ == '__main__':
          t = MyThread()
          t.start()
      
          print('主线程')
      

3.线程与进程之间的对比

  • 速度的对比

    • from threading import Thread
      
      
      def task(name):
          print(f'{name} is running')
      
      
      
      if __name__ == '__main__':
          t = Thread(target=task,args=('mcsaoQ',))
          t.start()
      
          print('主线程')
      
      '''
      线程绝对要比进程要快:
          mcsaoQ is running
          主线程
      '''
      
  • pid

    • # pid 进程号
      from threading import Thread
      import os
      
      def task():
          print(f'子线程: {os.getpid()}')
      
      
      
      if __name__ == '__main__':
          t = Thread(target=task,)
          t.start()
      
          print(f'主线程: {os.getpid()}')
      
  • 线程之间的共享资源

    • # from threading import Thread
      #
      # x = 1000
      #
      # def task():
      #     global x
      #     x = 0
      #
      #
      # if __name__ == '__main__':
      #     t = Thread(target=task)
      #     t.start()
      #     # t.join()
      #     print(f'主线程: {x}')
      
      
      
      
      # from threading import Thread
      # import time
      # x = 1000
      #
      # def task():
      #     time.sleep(3)
      #     print('子线程....')
      #
      #
      # def main():
      #     print('111')
      #     print('222')
      #     print('333')
      #
      # #
      # # task()
      # # main()
      #
      # if __name__ == '__main__':
      #     t = Thread(target=task)
      #     t.start()
      #     # t.join()
      #     main()
      

4.线程的其他方法

  • # from threading import Thread
    # import threading
    # import time
    #
    #
    # def task(name):
    #     time.sleep(1)
    #     print(f'{name} is running')
    #     print(threading.current_thread().name)
    #
    #
    #
    # if __name__ == '__main__':
    #     for i in range(5):
    #         t = Thread(target=task,args=('mcsaoQ',))
    #         t.start()
    #     # 线程对象的方法:
    #     # time.sleep(1)
    #     # print(t.is_alive())  # 判断子线程是否存活  ***
    #     # print(t.getName())  # 获取线程名
    #     # t.setName('线程111')
    #     # print(t.getName())  # 获取线程名
    #
    #     # threading模块的方法:
    #     # print(threading.current_thread().name)  # MainThread
    #     # print(threading.enumerate())  # 返回一个列表 放置的是所有的线程对象
    #     print(threading.active_count())  # 获取活跃的线程的数量(包括主线程)
    #     print('主线程')
    

5.守护线程

  • 守护:子线程守护主线程,

  • 结束条件:守护线程必须等待主线程结束才结束,主线程必须等所有的非守护线程结束才能结束

  • from threading import Thread
    
    import time
    
    def foo():
        print(123)
        time.sleep(3)
        print("end123")
    def bar():
        print(456)
        time.sleep(1)
        print("end456")
    if __name__ == '__main__':
        t1=Thread(target=foo)
        t2=Thread(target=bar)
    
        t1.daemon = True
        t1.start()
        t2.start()
        print("main-------")
    

6.互斥锁(同步锁)

7.死锁现象,递归锁

  • 进程由死锁与递归锁,与线程同理,

  • 死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去,此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁

    • from threading import Thread
      from threading import Lock
      import time
      
      lock_A = Lock()
      lock_B = Lock()
      
      
      class MyThread(Thread):
      
          def run(self):
              self.f1()
              self.f2()
      
          def f1(self):
              lock_A.acquire()
              print(f'{self.name}拿到A锁')
      
              lock_B.acquire()
              print(f'{self.name}拿到B锁')
              lock_B.release()
      
              lock_A.release()
      
          def f2(self):
              lock_B.acquire()
              print(f'{self.name}拿到B锁')
              time.sleep(0.1)
      
              lock_A.acquire()
              print(f'{self.name}拿到A锁')
              lock_A.release()
      
              lock_B.release()
      
      if __name__ == '__main__':
          for i in range(3):
              t = MyThread()
              t.start()
      
          print('主....')
      
  • 解决死锁的方法:递归锁,在python中为了支持同一线程中多次请求同一资源,Python提供了可重入锁RLock

    • RLock内部维护着一个Lock和counter变量,counter记录了acquire的次数,从而使得资源可以被多次require,直到一个线程所有的acquire都被release,其他线程才能获取资源,上面的例子如果使用RLock代替Lock,则不会发生死锁:

    • from threading import Thread
      from threading import RLock
      import time
      
      lock_A = lock_B = RLock()
      
      
      class MyThread(Thread):
          
          def run(self):
              self.f1()
              self.f2()
          
          def f1(self):
              lock_A.acquire()
              print(f'{self.name}拿到A锁')
              
              lock_B.acquire()
              print(f'{self.name}拿到B锁')
              lock_B.release()
              
              lock_A.release()
          
          def f2(self):
              lock_B.acquire()
              print(f'{self.name}拿到B锁')
              time.sleep(0.1)
              
              lock_A.acquire()
              print(f'{self.name}拿到A锁')
              lock_A.release()
              
              lock_B.release()
      
      
      if __name__ == '__main__':
          for i in range(10):
              t = MyThread()
              t.start()
          
          print('主....')
      

8.信号量

  • Semaphore管理一个内置的计数器,每当调用acquire()时内置计数器+1,调用release()时内置计数器-1,计数器不能小于0,当计数器为0时,acquire()将阻塞线程直到其他线程调用release(),

  • from threading import Thread
    from threading import Semaphore
    from threading import current_thread
    import time
    import random
    
    sem = Semaphore(5)
    
    def go_public_wc():
        sem.acquire()
        print(f'{current_thread().getName()} 上厕所ing')
        time.sleep(random.randint(1,3))
        sem.release()
    
    
    if __name__ == '__main__':
        for i in range(20):
            t = Thread(target=go_public_wc)
            t.start()
    

推荐阅读