概述

1、无法利用多核

Python的多线程是无法只用多核进行运算,(多进程可以) 【具体原因是GIL锁,取得GIL锁的进程才能运行,但一个进程的Python只有一个GIL】 【GIL 全局解释器锁】

2、无需通信

在各个子线程中,是可以直接调用到主线程的全局变量等 (可以把各个子线程看作同步运行的普通函数即可)

带来的问题: 假如有多条子线程同时运行,但他们又都会对全局变量A进行修改,且结果不同 这会导致全局变量A的结果随机 【为了放在这种事情发生,必须加一个Lock】


threading:线程模块

threading.Thread 线程类

用法和Process进程类基本差不多 创建的对象也有 strat( )、join( )方法,就连类的构造方法用法都基本差不多

【Thread类构造方法:threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)】 【Process类构造方法:Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)】 所以具体怎么用,去上一篇参考多进程即可

唯一的区别是,线程可以调用全局变量

  1. import threading
  2. def func():
  3. print("子进程")
  4. t = threading.Thread(target=func,args=())
  5. t.start()
  6. t.join()
  7. # 输出
  8. #=========================
  9. 子进程

threading.Lock 线程锁类

为了防止“多条子线程同时运行,又同时又对全局变量进行修改” 我们需要使用Lock类,将线程锁起来。

当线程拥有线程锁时,其他线程是无法被运行的,只有当线程锁被释放才能运行。 注意:一个进程只能拥有一个线程锁

类方法: acquire( ):加锁 release( ):释放锁

  1. import threading
  2. import random
  3. import time
  4. def func(x):
  5. time.sleep(random.randint(0,10)*0.1) #给进程一个随机增加运行时长
  6. print("子进程:",x)
  7. for i in range(10):
  8. t = threading.Thread(target=func,args=(i,))
  9. t.start()
  10. print("主线程")
  11. #输出
  12. #===============================
  13. 子进程: 5
  14. 主线程
  15. 子进程: 1
  16. 子进程: 6
  17. 子进程: 8
  18. 子进程: 4
  19. 子进程: 9
  20. 子进程: 2
  21. 子进程: 3
  22. 子进程: 0
  23. 子进程: 7

分析: 输出结果变得无序,那是因为所有的子线程可以看作是“同步执行的”

假如调用了一个全局变量,而且每个线程在结束前都对全局变量进行不同的修改, 这个变量最后的值将取决于最后一个执行完成的线程 但线程的运行时长是随机的,结果就导致全局变量结果是随机的

Lock的作用: 假如为线程添加一个Lock,当子线程运行时, 就必须等待上一个线程运行到锁被释放,下一个线程才能继续运行

  1. import threading
  2. import random
  3. import time
  4. mylock=threading.Lock()
  5. def func(x):
  6. mylock.acquire() #加线程锁
  7. time.sleep(random.randint(0,10)*0.1)
  8. print("子进程:",x)
  9. mylock.release() #释放线程锁
  10. for i in range(10):
  11. t = threading.Thread(target=func,args=(i,))
  12. t.start()
  13. time.sleep(3)
  14. print("主线程")
  15. # 输出
  16. #================================
  17. 子进程: 0
  18. 子进程: 1
  19. 子进程: 2
  20. 子进程: 3
  21. 子进程: 4
  22. 主线程
  23. 子进程: 5
  24. 子进程: 6
  25. 子进程: 7
  26. 子进程: 8
  27. 子进程: 9

主线程也是包括在内的, 在上一个线程锁释放之后,所有待运行的进程都会开始运行,包括主线程

+【关于GIL】

GIL是一个进程的全局线程锁,这个线程锁每隔一段时间就会释放一次,用于切换其他线程运行, 以此达到单核CPU运行多个线程的目的 但是是Python中一个进程只有一个GIL,那就意味着, 1、一个进程在同一时间只有一个线程在运行, 2、只是GIL释放和加锁的频率速度很快,让人误以为线程之间是同步进行的 3、Python的线程无法利用多核CPU

在Python2.7之前是可以通过sys.setcheckinterval( )方法来改变GIL的速度,但Python3已经弃用了这个方法

threading.local 本地储存

local类的作用: 将子线程的局部变量储存在,主线程中 使用local对象创建的变量,其他线程不可用(主线程中也一样)

如何使用Local类的对象去创建变量: 绑定

  1. import threading
  2. import random
  3. import time
  4. value_Local = threading.local()# 创建本地储存(用于保存线程的局部变量)
  5. value_Local.x = "不可见" # 主线程的局部变量(子线程不可见)
  6. def func(x):
  7. value_Local.y = x # 在线程中创建局部变量(其他线程不可见)
  8. print("子进程:",value_Local.y)
  9. for i in range(10):
  10. t = threading.Thread(target=func,args=(i,))
  11. t.start()

其实这一块我不是很理解, Local的作用是在主线程中创建一个子线程才能用的局部变量, 但事实上,我可以直接在子线程中创建局部变量。

因此我不是很理解Local的作用。 但我大致猜测,如果尝试在子线程新建一个局部变量, 有可能会导致一些问题,因此把创建的过程放在的主线程中。

这是官方文档的描述: (就这么点内容) QQ截图20210221213801.png