python的多线程(第三篇)关于全局变量的一些理解

首先作者在第一篇文章就说了,线程之间是共享全局变量的,具体体现在,我们编写的代码中,我们自己定义了一个顺序执行(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 版权协议,转载请附上原文出处链接和本声明。