Python线程的数据一致性问题

339 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情

我们知道Python中进程不可以实现进程间数据的共享,那线程是否可以呢,答案是可以,但是线程是数据共享的时候会出现数据不一致的问题,这是因为虽然数据的共享的,线程中使用恭喜那个共享数据的时候会保存一份在自己的线程中,处理完在刷新到共享数据变量中,这一过程就会出现数据不一致的问题。

共享变量的数据准确性

看着比较抽象,我们举例子说明一下,上代码:

import threading


# 全局变量
g_num = 0


def task():
    for i in range(1000000):
        global g_num  # 表示要声明修改全局变量的内存地址
        g_num += 1  
    print("task1:", g_num)


def task2():
    for i in range(1000000):
        global g_num  # 表示要声明修改全局变量的内存地址
        g_num += 1 

    print("task2:", g_num)


if __name__ == '__main__':
    first_thread = threading.Thread(target=task)
    second_thread = threading.Thread(target=task2)

    first_thread.start()
    second_thread.start()

这段代码是什么功能呢?它其实是将值从0加到2000000,这里使用了两个线程来完成

第一步,我们导入了线程模块,并且定义了一个全局变量

第二步定义两个方法,两个方法的功能都是一样的,都是对全局变量进行累加修改,这里需要注意的是,方法内修改全局变量需要添加global关键字,表示我要声明需要修改这个全局变量的内存地址了。

第三步就是在main函数中创建两个线程,进行启动线程。

这里的结果并不是2000000,原因在于两个线程同时对一个变量进行操作的时候并没有进行加锁操作,这就有可能线程1修改的值,在线程2中并没有拿到。

线程顺序执行

想让结果变为2000000,我们可以在第一个线程启动后的代码后加入 first_thread.join() 这样表示在第一个线程完成后再执行主函数后的代码,也就是线程2在进行启动,这就保证了线程的按顺序启动。

互斥锁

除了这种方法外,还可以使用互斥锁来保证共享变量修改的有效性,具体方法是先定义一个全局互斥锁lock = threading.Lock() 然后在方法内修改变量前先获取锁:lock.acquire() ,修改变量结束后释放锁:lock.release() 通过这种互斥锁的方式,保证同一时刻只能有一个线程去操作共享数据,从而保证了结果的正确,使用互斥锁也会带来一个新的问题,就是可能出现死锁的现象,这是我们在开发的过程中需要注意的。

小结

这篇文章我们Python使用多线程修改共享变量的时候可能出现数据不准确的情况,解决这种问题我们提出了两种方式,一种是使用互斥锁,一种是使用join()方法,保证线程的有序执行。