先看上一节的代码, 我们再改良下,看看是什么效果?
下边的代码,细心的小伙伴可以看出, 本来是多线程 却让你逼成单线程,
因为 在加锁和释放锁 都是在循环计算的外边,也就是 在线程一 执行完后 再执行 线程二,
import threading ,time
def work1():
global num
mutex.acquire() # 上锁
for i in range(100000):
num += 1
mutex.release() # 开锁
print("work"+str(num) )
def work2():
global num
mutex.acquire() # 上锁
for i in range(100000):
num += 1
mutex.release() #释放
print("work2 %s"%num)
if __name__=='__main__':
mutex=threading.Lock() # 创建一个锁对象
num=0
t1 = threading.Thread(target=work1)
t1.start() #线程1 运行
t2 = threading.Thread(target=work2)
t2.start() # 线程2 运行
我们改下下, 把锁加在计算里面 ,看看是什么效果?
import threading ,time
def work1():
global num
for i in range(100000):
mutex.acquire()
num += 1
mutex.release()
print("work"+str(num) )
def work2():
global num
for i in range(100000):
mutex.acquire()
num += 1
mutex.release()
print("work2 %s"%num)
if __name__=='__main__':
mutex=threading.Lock()
num=0
t1 = threading.Thread(target=work1)
t1.start() #线程1 运行
t2 = threading.Thread(target=work2)
t2.start() # 线程2 运行
print("main%d"%num)
打印结果为:
main 25083
work2 196207
work 200000
这样就可以他们两个一起执行 , 在关键点加,例如改变共享变量的地方。
当时我看到 这样结果 ,我有以下问题 ,你也会遇到的。
1. work1 为什么放到最后才给结果 ,难道不是work2放到最后
2. work2 执行结束后怎么是 196207 而不是 100000
3. 和轮询等待方式一样吗? 难道不消耗 cpu
4. 多线程的锁有执行顺序吗 ?
5.加锁的位置选择?
6. main 打印的为什么和其他都不一样
我们先分析下执行过程 ?
-
主线程运行下边的代码,生成一个锁对象,然后初始化num的值
mutex=threading.Lock()
num=0 -
主线程继续走, 遇到下边的代码 同时生成两个子线程
现在为止 一共有三个线程他们同时在跑,一个是 主线程和两个子线程 , 主线程是不等子线程跑完再跑
t1 = threading.Thread(target=work1)
t1.start() #线程1 运行
t2 = threading.Thread(target=work2)
t2.start() # 线程2 运行 -
两个子线程 生成后,当遇到有加锁的地方,两个线程开始争夺控制权,争夺到的,立刻拿到了开锁的控制权,对其他的线程来说是关闭的,所以只能运行有锁控制权的代码(以下代码)
for i in range(100000):
mutex.acquire()
num += 1
mutex.release() -
当运行一次 释放控制权的时候,他会通知其他线程,我的锁已经释放了,再继续争夺控制权, 这个过程是通知, 在等通知期间是休息的,不占用cpu 资源
-
主线程是不等子线程跑完再跑, 继续执行 print(“main%d”%num), 这个num 当时计算的结果是什么就是什么,
-
主线程执行完毕后,不会退出直到所有的子线程完成。
-
子线程循环执行后,当有一个线程执行完毕就会执行打印当前线程累加的值 print(“work2 %s”%num),所以他的结果是两个线程循环计算的结果不是单个线程的结果。
-
线程1 结束后, 线程二继续单线程的跑,直到结束 , 由于他们避免了同时操作一个值的过程,所以计算的值 是累加起来的 200000
1. work1 为什么放到最后才给结果 ,难道不是work2放到最后
答: 有可能work2抢占控制权比较多,他就先执行 打印出来了, 这个是随机的。
2. work2 执行结束后怎么是 196207 而不是 100000
答: 当线程结束后,就打印这个值,这个值是线程之间轮循累加的结果,不是单线程跑的结果。
3. 和轮询等待方式一样吗? 难道不消耗 cpu
答: 在一个线程抢占了锁, 其他线程是sleep 状态, sleep 不消耗资源,他释放锁的时候,会叫醒其他睡着的线程。
4. 多线程的锁有执行顺序吗 ?
答: 是随机的, 有操作系统调度算法决定。 他也有一个准则就是 雨露均沾。
5.加锁的位置选择?
答: 粒度越小越好, 在业务执行的点加 例如在改变全局变量的地方 , 就像上边的例子,当放到 for循环里面和外边差别很大
6. main 打印的为什么和其他都不一样
答: 主线程是不等子线程跑完再跑, 继续执行 print(“main%d”%num), 这个num 当时计算的结果是什么就打印什么,