问题抛出:求100一直减到0?
可以用常规的串行方式解决,这里就不讨论了。我们直接上手多线程同时计算100连续减到0。
import threading # 导入线程模块
def func():
global num
# num -= 1 这个结果是正确的 0
temp = num # 这个执行结果是99 为什么不是0? 答:对于num-=1 cpu处理速度非常快 而换成这种中间变量方式则增加了cpu处理时间 尤其是有阻塞的time.sleep
time.sleep(0.1) # 因为GIL导致Python每次只有一个线程在cpu上跑
num = temp - 1 # 因此每次cpu遇到阻塞就切换另一个线程,另一个线程也是如此。直到切换完100次 回到第一次的temp=100 执行100-1 剩下的99线程也是如此。
list_thread = []
num = 100
for i in range(100): # 创建100个线程
t = threading.Thread(target=func)
t.start()
list_thread.append(t)
for t in list_thread: # 让线程执行完了再打印最后的数字
t.join()
print("End num:", num)
思考:对于本需求的抛出,那么Python就没有办法用多线程解决了吗?
答:有! 那就是用同步锁!
这需要引入同步锁
在代码num=100下面引入:
lock = threading.Lock() # 引入同步锁
在func中加入同步锁:
lock.acquire() # 加锁
temp = num
time.sleep(0.1)
num = temp - 1
lock.release() # 解锁
加锁的目的就是告诉cpu:在我加锁的这段代码没有执行完之前不准切换。 这就解决了问题啦!
思考:既然加锁了那么就相当于程序变成了串行不允许cpu切换,这和多线程有啥关系?
答:没错!加锁确实是变成了串行,但是这只是其中加锁部分的代码变成了串行,对于没有加锁的代码而言依旧是多线程并行的。例如在加锁代码之前加入 print("ok"),还是多线程!一瞬间同时打印100个ok。
思考:GIL是把锁了,为什么还有整个同步锁?
答:两把锁功能不一样。GIL这把锁是Python语言的开发者设计的,目的是让同一时间只能有一个线程在Cpython解释器上执行,防止变量等公共资源被杂七杂八的线程共同使用。而同步锁是我们程序员自己设定的,目的是控制cpu要一直执行某段代码,不允许中途切换造成计算失误。