python多线程学习笔记(超详细)

python
threading
多线程

一. Threading简介

首先看下面的没有用Threading的程序

  1. import threading,time 
  2.  
  3. def fun(): 
  4. s = 0 
  5. for i in range(30): 
  6. s += i 
  7. time.sleep(0.1
  8. print(s) 
  9.  
  10. if __name__ == '__main__'
  11. t = time.time() 
  12. fun() 
  13. fun() 
  14. print(time.time()-t) 
  15.  
  16. >>>  
  17. 435 
  18. 435 
  19. 6.023701906204224 
  20. [Finished in 6.6s] 

如果使用线程会有什么样的效果呢

  1. import threading,time 
  2.  
  3. def fun(): 
  4. s = 0 
  5. for i in range(30): 
  6. s += i 
  7. time.sleep(0.1
  8. print(s) 
  9.  
  10. if __name__ == '__main__'
  11. # 创建了一个线程列表,包含2个线程 
  12. ths = [threading.Thread(target=fun) for i in range(2)] 
  13. for th in ths: 
  14. th.start() 
  15. t = time.time() 
  16. for th in ths: 
  17. th.join() 
  18. print(time.time()-t) 
  19.  
  20.  
  21. >>>  
  22. 435 
  23. 435 
  24. 3.116874933242798 
  25. [Finished in 3.7s] 

这说明两个线程几乎是同时进行的


二. Threading的应用进阶

  1. join(timeout)用来实现线程等待。
    被调用join()方法的线程会一直阻塞调用者的线程,
    直到自己结束(正常结束,或引发未处理异常),
    或超出timeout的时间。

  1. import threading,time 
  2.  
  3. class MyThread(threading.Thread): 
  4.  
  5. def run(self): 
  6. for i in range(30): 
  7. print('threading:',i) 
  8. time.sleep(0.1
  9.  
  10. if __name__ == '__main__'
  11. t = MyThread() 
  12. t.start() 
  13. t.join(1
  14. for i in range(10): 
  15. print('Main:',i) 
  16. time.sleep(0.1
  17.  
  18. >>>  
  19. threading: 0 
  20. threading: 1 
  21. threading: 2 
  22. 主线程等待t这个线程0.1秒后也开始运行 
  23. Main: 0 
  24. threading: 3 
  25. Main: 1 
  26. threading: 4 
  27. Main: 2 
  28. threading: 5 
  29. Main: 3 
  30. threading: 6 
  31. Main: 4 
  32. threading: 7 
  33. Main: 5 
  34. threading: 8 
  35. Main: 6 
  36. threading: 9 
  37. Main: 7 
  38. Main: 8 
  39. Main: 9 
  40. [Finished in 2.0s] 

注意每次运行的结果都不太一样

2)daemon属性
被设定为后台运行的线程,会在主程序退出时主动自杀。
设置为后台运行线程的方法是:设置线程的daemon属性为True

  1. import threading,time 
  2.  
  3. def dmn(): 
  4. print('dmn start...'
  5. time.sleep(2
  6. print('dmn end.'
  7.  
  8. def ndmn(): 
  9. print('ndmn start...'
  10. time.sleep(1
  11. print('ndmn end.'
  12.  
  13. d = threading.Thread(target=dmn) 
  14. d.daemon = True 
  15. n = threading.Thread(target=ndmn) 
  16. print('start...'
  17. d.start() 
  18. n.start() 
  19. print('end.'
  20.  
  21. >>> 
  22. start... 
  23. dmn start... 
  24. ndmn start... 
  25. end. 
  26. ndmn end. 
  27. [Finished in 1.3s] 

由上面打印的结果我们可以看到dmn线程设置为后台线程后,它的 print('dmn end.') 语句并不没有执行,这是因为后台线程在主线程结束后会自杀,所以主线程执行完后,dmn线程没能说出自己的“遗言”。
作为对比,我将daemon设为False,结果如下

  1. ... 
  2.  
  3. d = threading.Thread(target=dmn)  
  4. d.daemon = False  
  5. ... 
  6.  
  7. >>> 
  8. start... 
  9. dmn start... 
  10. ndmn start... 
  11. end. 
  12. ndmn end. 
  13. dmn end. 
  14. [Finished in 2.5s] 
  1. 线程同步
    1 )指令锁 threading.Lock

acquire尝试获得锁定,进入阻塞状态。
acquire(blocking=True, timeout=-1))

release释放获得锁定(资源使用完后)
release()

  1. import threading,time,random 
  2.  
  3. share = 4  
  4. lock = threading.Lock() #初始化指令锁 
  5.  
  6. class MyThread(threading.Thread): 
  7. def __init__(self,i): 
  8. super().__init__() 
  9. self.i = i 
  10.  
  11. def run(self): 
  12. global share 
  13. for d in range(2): 
  14. lock.acquire() 
  15. print(share) 
  16. share += self.i 
  17. time.sleep(random.random()) 
  18. print('+',self.i,'=',share) 
  19. lock.release() 
  20.  
  21. if __name__ == '__main__'
  22. t = MyThread(2
  23. tt = MyThread(6
  24. t.start() 
  25. tt.start() 
  26.  
  27. >>> 
  28. 4 
  29. + 2 = 6 
  30. 6 
  31. + 6 = 12 
  32. 12 
  33. + 2 = 14 
  34. 14 
  35. + 6 = 20 
  36. [Finished in 2.9s] 

为了更好的感受指令锁的作用,将acquire和release去掉后结果如下

  1. ... 
  2. def run(self):  
  3. global share  
  4. for d in range(2):  
  5. # lock.acquire()  
  6. print(share)  
  7. share += self.i  
  8. time.sleep(random.random())  
  9. print('+',self.i,'=',share)  
  10. # lock.release()  
  11. ... 
  12.  
  13. >>> 
  14. 4 
  15. 6 
  16. + 6 = 12 
  17. 12 
  18. + 2 = 18 
  19. 18 
  20. + 6 = 20 
  21. + 2 = 20 
  22. [Finished in 2.2s] 

比较后可以知道,加了指令锁后可以清楚地知道对共享资源share操作的具体情况

2 )条件变量threading.Condition
属性

  • 实例化时,可指定锁。

  • acquire()

  • release()

  • wait(timeout=None)
    释放锁,进入等待阻塞,
    直到唤醒或超时。

  • notify(n=1)
    唤醒等待该条件变量的线程。默认1个。

  • notify_all()
    唤醒等待该条件变量的所有线程。

实现严格的依照次序操作的线程之间的通信。
典型的实例:生产者/消费者(只有生产后,才能消费)。
线程之间可以互相通知,以达到默契的配合。
条件变量可以使用默认的锁或用户创建的锁来工作。

话不多说看代码

  1. import threading,time 
  2.  
  3. share = 0 
  4.  
  5. share_cond = threading.Condition() 
  6.  
  7. # 生产者 
  8. class ProThread(threading.Thread): 
  9. def __init__(self): 
  10. super().__init__() 
  11. self.name = 'Produce' 
  12.  
  13. def run(self): 
  14. global share 
  15. if share_cond.acquire(): 
  16. while True
  17. if not share: # 若没东西了,即开始生产 
  18. share += 1 
  19. print(self.name,share) 
  20. share_cond.notify() #唤醒消费者?这个是我自己的理解  
  21. share_cond.wait() 
  22. time.sleep(1
  23.  
  24. # 消费者 
  25. class CustomThread(threading.Thread): 
  26. def __init__(self): 
  27. super().__init__() 
  28. self.name = 'Custom' 
  29.  
  30. def run(self): 
  31. global share 
  32. if share_cond.acquire(): 
  33. while True
  34. if share: 
  35. share -= 1 # 若有东西就买买买 
  36. print(self.name,share) 
  37. share_cond.notify() #唤醒生产者,同上,仅是个人理解,如有错请告知,谢谢 
  38. share_cond.wait() 
  39. time.sleep(1
  40.  
  41. if __name__ == '__main__'
  42. t = ProThread() 
  43. tt = CustomThread() 
  44. t.start() 
  45. tt.start() 
  46.  
  47. >>> 
  48. Produce 1 
  49. Custom 0 
  50. Produce 1 
  51. Custom 0 
  52. Produce 1 
  53. Custom 0 
  54. ... 
  55. ... 
  56. ... 

上面的结果会一直重复执行下去

3 ) 信号量threading.Semaphore

属性

  • 实例化时,指定使用量。

  • 其内置计数器,锁定时+1,
    释放时-1,计数器为0则阻塞。

  • acquire(blocking=True,timeout=None)

  • release()释放锁。

  1. import threading,time 
  2.  
  3. sema = threading.Semaphore(2
  4.  
  5. class MyThread(threading.Thread): 
  6. def __init__(self,name): 
  7. super().__init__() 
  8. self.name = name 
  9.  
  10. def run(self): 
  11. if sema.acquire(): 
  12. print(self.name,'Had got resource.'
  13. time.sleep(1
  14. sema.release() 
  15. print(self.name,'Had released resource.'
  16.  
  17. if __name__ == '__main__'
  18. ths = [MyThread(str(i)+'Sema') for i in range(5)] 
  19. for th in ths: 
  20. th.start() 
  21.  
  22. >>> 
  23. 0Sema Had got resource. 
  24. 1Sema Had got resource. 
  25. 2Sema Had got resource. 
  26. 1Sema Had released resource. 
  27. 3Sema Had got resource. 
  28. 0Sema Had released resource. 
  29. 3Sema Had released resource. 
  30. 4Sema Had got resource. 
  31. 2Sema Had released resource. 
  32. 4Sema Had released resource. 
  33. [Finished in 3.6s] 

4 ) 线程通信threading.Event

  • 其管理一个内部标志.实现一个线程,唤醒其它线程。

  • set() 设置内部标志为True

  • clear() 设置内部标志为False

  • wait(timeout)
    阻塞线程,到内部标志为True。

  1. import threading,time 
  2.  
  3. event = threading.Event() 
  4.  
  5. class MyThreadWait(threading.Thread): 
  6. def run(self): 
  7. self.name = 'Wait Thread' 
  8. print(self.name,"Wait..."
  9. event.wait() 
  10. print(self.name,"Start..."
  11. event.clear() 
  12.  
  13. class MyThreadMain(threading.Thread): 
  14. def run(self): 
  15. time.sleep(3
  16. print('Main thread set event flag!'
  17. event.set() 
  18.  
  19. if __name__ == '__main__'
  20. thw = MyThreadWait() 
  21. thm = MyThreadMain() 
  22. thw.start() 
  23. thm.start() 
  24.  
  25. >>> 
  26. Wait Thread Wait... 
  27. Main thread set event flag! 
  28. Wait Thread Start... 
  29. [Finished in 3.6s] 

好了,大概就是这些了,其他的以后再补充,另外感谢麦子学院提供的免费课程~~~真心不是打广告哈哈
为了避嫌,顺便感谢一下imooc,极客学院~很多都是从这些造福人类的网站学到的。
另附上麦子学院的视频教程,毕竟要学会感恩嘛
http://www.maiziedu.com/course/644-9663/


marsggbo笔记出品,必属精品哈哈

原文地址:https://www.cnblogs.com/marsggbo/p/6622880.html