threading模块包含下面的类:
- Thread:基本线程类
- Lock:互斥锁
- RLock:可重入锁,使单一进程再次获得已持有的锁(递归锁)
- Condition:条件锁,使得一个线程等待另一个线程满足特定条件,比如改变状态或某个值。
- Semaphore:信号锁。为线程间共享的有限资源提供一个”计数器”,如果没有可用资源则会被阻塞。
- Event:事件锁,任意数量的线程等待某个事件的发生,在该事件发生后所有线程被激活
- Timer:一种计时器
- Barrier:Python3.2新增的“阻碍”类,必须达到指定数量的线程后才可以继续执行。
thread
是低级模块,threading
是高级模块,绝大多数情况下,我们只需要使用threading
这个高级模块
方法:
join()方法:希望主线程等等子线程,不要“埋头往前跑”
setDaemon(True):把所有的子线程都变成主线程的守护线程,当主线程结束后,守护子线程也会随之结束,整个程序也跟着退出
Python针对不同类型的任务,多线程执行效率是不同的:
Python下的多线程对CPU密集型任务并不友好,对IO密集型任务比较友好。
全局解释器锁(GIL),为什么不能去掉GIL?
GIL:任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。
GIL的移除给单线程程序的执行速度带来了一定的负面影响
线程和进程都是CPU工作时段的描述
进程是爹妈,管着众多的线程儿子
进程:独立功能的程序,系统分配资源的一个独立单位,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响
线程:进程的一个实体,CPU调度的基本单位,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改
因此:为了避免多线程数据的紊乱,提出了线程锁,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。
eg:
lock = threading.Lock()
lock.acquire()
lock.release()
协程: 协程的调度完全由用户控制,
多线程数据错乱案例:
import time, threading # 假定这是你的银行存款: balance = 0 def change_it(n): # 先存后取,结果应该为0: global balance # print('n',n) balance = balance + n balance = balance - n def run_thread(n): for i in range(1000000): change_it(n) if __name__ == '__main__': t1 = threading.Thread(target=run_thread, args=(5,)) t2 = threading.Thread(target=run_thread, args=(8,)) t1.start() t2.start() print (balance) t1.join() t2.join() print (balance)
结果:
0
5
加锁后:
import time, threading lock=threading.Lock() # 假定这是你的银行存款: balance = 0 def change_it(n): # 先存后取,结果应该为0: global balance # print('n',n) balance = balance + n balance = balance - n def run_thread(n): for i in range(1000000): lock.acquire() change_it(n) lock.release() if __name__ == '__main__': t1 = threading.Thread(target=run_thread, args=(5,)) t2 = threading.Thread(target=run_thread, args=(8,)) t1.start() t2.start() print (balance) t1.join() t2.join() print (balance)
结果:
0
0