首先作者在第一篇文章就说了,线程之间是共享全局变量的,具体体现在,我们编写的代码中,我们自己定义了一个顺序执行(join()),那么就不会出错,但是当同时进行的时候,就会出错,上篇文章举过一个小案例
1,多线程同时对全局变量进行操作
import threading
# 定义全局变量
g_num = 0
# 循环一次给全局变量加1
def sum_num1():
for i in range(1000000):
global g_num
g_num += 1
print("sum1:", g_num)
# 循环一次给全局变量加1
def sum_num2():
for i in range(1000000):
global g_num
g_num += 1
print("sum2:", g_num)
if __name__ == '__main__':
# 创建两个线程
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
# 启动线程
first_thread.start()
# 启动线程
second_thread.start()
- 执行结果
sum1: 1210949
sum2: 1496035
1.1这里就是我之前说的数据发生了偏差
2,原因分析
1,在第一次第一个函数在执行的时候,还没有增加,系统调度为”sleeping“状态,把下一个线程转换为”running“,对数据进行增加
2,当函数二结束的时候,函数一再执行的时候,其实数据变化了,但是函数一刚开始执行的时候,数据还是原始的,所以偏差就发生在这里
3,解决办法
3.1,线程等待
- join() 也是我之前说的阻塞状态,等待当前线程执行完了之后,在执行下一个线程
- 执行效果
sum1: 1000000
sum2: 2000000
3.1.1,当我们使用join()方法的时候,其实我们人为的将多线程,变为了单线程
3.2, 互斥锁
3.2.1,概念:对共享数据进行锁定,保证同一时刻只有一个线程去操作
1,抢到锁的线程先执行,没有抢到锁的线程需要等待,等锁用完后需要释放,然后其他等待的线程再去抢夺这个锁,哪个线程抢到哪个线程再执行
2,具体那个线程抢到这个锁我们决定不了,是有cpu调度决定的
3.2.1,怎么使用
# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
- 1,如果没有创建这个锁,acquire是不会堵塞的
- 2,如果在调用acquire对这个锁上锁之前 它已经被其他线程上了锁,那么此时acquire会堵塞,知道这个锁被解锁为止
import threading
# 定义全局变量
g_num = 0
# 创建全局互斥锁
lock = threading.Lock()
# 循环一次给全局变量加1
def sum_num1():
# 上锁
lock.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("sum1:", g_num)
# 释放锁
lock.release()
# 循环一次给全局变量加1
def sum_num2():
# 上锁
lock.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("sum2:", g_num)
# 释放锁
lock.release()
if __name__ == '__main__':
# 创建两个线程
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
# 启动线程
first_thread.start()
second_thread.start()
- 加上互斥锁多任务会直接变成单任务,性能会降低
- 如果不释放锁的话,程序会进入死锁,不会进入下一个线程,同理死循环的也会造成死循环
4,原理
当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。
每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。
线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。
5,总结
好处
- 确保了某段代码只能有一个线程从头到尾完整的执行
坏处
- 多线程执行变成了包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
- 锁使用不好就容易出现死锁的情况
版权声明:本文为weixin_44174099原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。