zoukankan      html  css  js  c++  java
  • 多线程

    线程

    什么是线程

    程序在运行代码的过程。

    进程与线程

    进程:一个在运行中的程序(具体内容是由线程来运行)的状态,是系统的一个资源单位。

    线程:运行指定代码的过程。一个进程中至少有一个线程,但是一个进程可以有多个线程。线程是cpu调度的最小单位。

    类比到现实中就是生产车间(指代进程)与流水线(指代线程)的关系。我们可以说这个车间是生产汽车,生产车间里面的每一条流水线都是生产汽车部件的。生产车间是由一个一个流水线组合成地。进程也是由线程构成地。

    线程与进程的区别

    1. 进程之间是内存隔离,线程之间是共享内存
    2. 创建进程由于需要开辟内存空间,而线程不需要开辟内存空间,所以创建线程的速度比创建进程的速度快

    线程创建的两种方式

    使用threading模块创建Thread,而且创建线程的方式与multiprocessing创建线程的方式一样(据说是multiprocessing照着threading写的,不要说是我说的)

    • 方式一:实现Thread类

      from threading import Thread,currentThread
      
      def pr():
          print(f'我是进程{currentThread().name}!')
      
      t = Thread(target=pr)
      t.start()
      print('我是主进程!')
      #######################
      # 结果:
      我是进程Thread-1!   # 因为创建线程的速度很快所以创建了线程就执行了代码
      我是主进程!
      
    • 方式二:继承Thread类

      from threading import Thread,currentThread
      
      class MyT(Thread):
          def run(self) -> None:   # 必须重写run方法
              print(f'我是进程{currentThread().name}')
      
      t = MyT()
      t.start()
      print('我是主进程!')
      #######################
      # 结果:
      我是进程Thread-1!
      我是主进程!
      

    线程的属性和方法

    • join

      主线程等待子线程运行结束

      from threading import Thread
      import time
      
      def task():
          print('子线程开启!')
          time.sleep(2)
          print('子线程结束!')
      
      t = Thread(target=task)
      t.start()
      print('主线程结束!')
      ######################
      # 结果:
      子线程开启!
      主线程结束!
      子线程结束!
      
    • isAlive()

      线程是否还在运行

      from threading import Thread
      import time
      
      def task():
          print('子线程开启!')
          time.sleep(2)
          print('子线程结束!')
      
      t = Thread(target=task)
      t.start()
      time.sleep(1)
      print(t.isAlive())   # True
      print('主线程结束')
      
    • getName()

      获取线程名字

      from threading import Thread
      import time
      
      def task():
          print('子线程开启!')
          time.sleep(2)
          print('子线程结束!')
      
      t = Thread(target=task)
      t.start()
      print(t.getName())   # Thread-1
      print('主线程结束')
      
    • setName()

      设置线程名字

      from threading import Thread
      import time
      
      def task():
          print('子线程开启!')
          time.sleep(2)
          print('子线程结束!')
      
      t = Thread(target=task)
      t.start()
      print(t.getName())   # Thread-1
      t.setName('张三')
      print(t.getName())   # 张三
      print('主线程结束')
      
    • currentThread()

      获取当前线程对象

      from threading import Thread,currentThread
      import time
      
      def task():
          print(currentThread().getName())   # Thread-1
          currentThread().setName('张三')
          print(currentThread().getName())   # 张三
      
      t = Thread(target=task)
      t.start()
      time.sleep(0.5)
      print(t.getName())   # 张三
      print('主线程结束')
      
    • enumerate()

      返回当前活动进程

      from threading import Thread,enumerate
      import time
      
      def task():
          print('子线程开启!')
          time.sleep(2)
          print('子线程结束!')
      
      t = Thread(target=task)
      t.start()
      print(enumerate())   # [<_MainThread(MainThread, started 3068)>, <Thread(Thread-1, started 16348)>]
      print('主线程结束')
      
    • activeCount()

      返回当前活动线程数量

      from threading import Thread,activeCount
      import time
      
      def task():
          print('子线程开启!')
          time.sleep(2)
          print('子线程结束!')
      
      t = Thread(target=task)
      t.start()
      print(activeCount())   # 2
      print('主线程结束')
      

    守护线程

    守护线程守护至当前进程结束

    from threading import Thread
    import time
    
    def task():
        print('子线程开启!')
        time.sleep(2)
        print('子线程结束!')
    
    t = Thread(target=task)
    t.daemon = True
    t.start()
    
    print('主线程结束')
    

    但是,主进程会等待还没有结束的子线程结束后结束

    from threading import Thread
    import time
    
    def task():
        print('守护线程开启!')
        time.sleep(2)
        print('守护线程结束!')
    
    def pr():
        print('子线程开启!')
        time.sleep(3)
        print('子线程结束!')
    
    t = Thread(target=task)
    t.daemon = True
    t.start()
    t = Thread(target=pr)
    t.start()
    
    print('主线程结束')
    

    线程锁

    当我们要对线程间共享的资源进行操作,为了资源的安全考虑,就需要为资源加上线程锁。这样就可以保证同一个资源同一时刻只有一个线程有权限去修改。

    from threading import Lock,Thread
    import time
    #初始化一把锁,由于线程间共享内存所以可以不用传递参数
    lock=Lock()
    count = 1
    
    def buy():
        #获取钥匙在acquire()和release()之间的代码在同一时间只允许一个进程执行,当一个进程完成后将钥匙归还后,其他的进程才可以获得钥匙。
        lock.acquire()  # 拿锁
        # 模拟网络的延迟
        global count
        time.sleep(0.1)
        if count>0:
            print('买到票了')
            count=count-1
            with open('access.txt', 'w', encoding='utf-8') as f:
                f.write(str(count))
        else:
            print('没票了')
        lock.release()  #还锁
    
    count = 1
    for i in range(5):
        p=Thread(target=buy)
        p.start()
    

    死锁

    当程序中有两把锁,并且一个线程需要拿到两把锁才能运行下去的时候,就会发生死锁的问题

    from threading import Lock,Thread,currentThread
    import time
    
    lock1 = Lock()
    lock2 = Lock()
    
    def func1():
        lock2.acquire()
        print('33[41m%s 拿到A锁33[0m' % currentThread().name)
    
        lock1.acquire()
        print('33[42m%s 拿到B锁33[0m' % currentThread().name)
        lock1.release()
    
        lock2.release()
    
    def func2():
        lock1.acquire()
        print('33[43m%s 拿到B锁33[0m' % currentThread().name)
        time.sleep(2)
    
        lock2.acquire()
        print('33[44m%s 拿到A锁33[0m' % currentThread().name)
        lock2.release()
    
        lock1.release()
    
    t1 = Thread(target=func2)
    t2 = Thread(target=func1)
    t1.start()
    t2.start()
    

    解决死锁问题可以使用 递归锁

    递归锁

    在一个线程中是的锁可以被多次获取,但是不同线程间只能有一个线程获得锁

    from threading import RLock,Thread,currentThread
    import time
    
    lock1 = RLock()
    lock2 = lock1
    
    def func1():
        lock2.acquire()
        print('33[41m%s 拿到A锁33[0m' % currentThread().name)
    
        lock1.acquire()
        print('33[42m%s 拿到B锁33[0m' % currentThread().name)
        lock1.release()
    
        lock2.release()
    
    def func2():
        lock1.acquire()
        print('33[43m%s 拿到B锁33[0m' % currentThread().name)
        time.sleep(2)
    
        lock2.acquire()
        print('33[44m%s 拿到A锁33[0m' % currentThread().name)
        lock2.release()
    
        lock1.release()
    
    t1 = Thread(target=func2)
    t2 = Thread(target=func1)
    t1.start()
    t2.start()
    

    GIL(全局解释器锁)

    因为cpython自带的垃圾回收机制不是线程安全的,所以要有GIL锁来保证同一时间只有一个线程在解释器中运行。但是这会导致cpython中的多线程并不是真正意义上的多线程,它只能运用到单核的性能。

    解决这个问题主要有两种办法:

    1. 使用多线程
    2. 使用C语言写多线程的部分,再使用ctypes模块

    具体可以看:https://blog.csdn.net/IAlexanderI/article/details/72932309

    线程间通信

    1. 队列

      先进先出

      import queue
      q = queue.Queue()
      q.put('123')
      q.put('qweqwe')
      print(q.get())
      print(q.get())
      ###############
      # 结果:
      123
      qweqwe
      
    2. 后进先出

      import queue
      q = queue.LifoQueue()
      q.put('123')
      q.put('qwe')
      q.put('asd')
      print(q.get())
      print(q.get())
      print(q.get())
      ################
      # 结果:
      asd
      qwe
      123
      
    3. 优先级

      按照元组中的优先级输出

      import queue
      q = queue.PriorityQueue()
      q.put((4,'123'))
      q.put((2,'qwe'))
      q.put((3,'asd'))
      print(q.get())
      print(q.get())
      print(q.get())
      ################
      # 结果:
      (2, 'qwe')
      (3, 'asd')
      (4, '123')
      

    线程定时器

    倒计时,当时间归零开始执行线程

    from threading import Timer
    
    def task():
        print('线程开始执行')
    
    t = Timer(4,task) # 过了4s后开启了一个线程
    t.start()
    
  • 相关阅读:
    css浮动
    css各种元素最原始的表现
    css3 unset属性
    js类式继承
    javascript编写Tab选项卡
    javaScript事件冒泡
    javascript中的&&与||的用法
    比较好的前端网站
    原生js开发tab选项卡之闭包
    冒泡排序(中级版)
  • 原文地址:https://www.cnblogs.com/Gredae/p/11570308.html
Copyright © 2011-2022 走看看