python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > python互斥锁与死锁

python多线程互斥锁与死锁问题详解

作者:陈小c

大家好,本篇文章主要讲的是python多线程互斥锁与死锁问题详解,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下

一、多线程共享全局变量

代码实现的功能:

创建work01与worker02函数,对全局变量进行加一操作创建main函数,生成两个线程,同时调用两个函数

代码如下:

import threading

result = 0  # 定义全局变量result
def work1(num):
    global result
    for i in range(num):
        result += 1
    print('------from work1-------', result)


def work2(num):
    global result
    for i in range(num):
        result += 1
    print('------from work2-------', result)


def main():
    print('--------begin--------', result)
    # 创建两个线程
    t1 = threading.Thread(target=work1, args=(1000,))
    t2 = threading.Thread(target=work1, args=(1000,))
    t1.start()
    t2.start()

if __name__ == '__main__':
    main()

运行结果:

--------begin---------- 0
------from work1------- 1000
------from work1------- 2000

两个线程之间共享了全局变量result,但是当我们把range的数值调大一些,

    t1 = threading.Thread(target=work1, args=(1000000,))
    t2 = threading.Thread(target=work1, args=(1000000,))

我们再来看一下结果,这是为什么呢,我们往下看一下结果:

--------begin---------- 0
------from work1------- 1358452
------from work1------- 1696352

总结:
在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)

二、给线程加一把锁锁

假如当前 g_num 值是100,当线程1执行第一步时,cpu通过计算获得结果101,并准备把计算的结果101赋值给g_num,然后再传值的过程中,线程2突然开始执行了并且执行了第一步,此时g_num的值仍未100,101还在传递的过程中,还没成功赋值,线程2获得计算结果101,并准备传递给g_num,经过一来一去这么一折腾,分明做了两次加 1 操作,g_num结果却是101,误差就由此产生,往往循环次数越多,产生的误差就越大,此时我们可以加一把锁。

acquire() — 锁定资源,此时资源是锁定状态,其他线程无法修改锁定的资源,直到等待锁定的资源释放之后才能操作;

release() — 释放资源,也称为解锁操作,对锁定的资源解锁,解锁之后其他线程可以对资源正常操作;

以上面的代码为列子:想得到正确的结果,可以直接利用互斥锁在全局变量 加1 之前 锁定资源,然后在计算完成之后释放资源,这样就是一个完整的计算过程,至于应该是哪个线程先执行,无所谓,先到先得,凭本事说话….演示代码如下:

# 开发时间:2022-01-27 12:59
import threading

result = 0  # 定义全局变量result
mutex = threading.Lock()

def work1(num):
    global result
    mutex.acquire()
    for i in range(num):
        result += 1
    print('------from work1-------', result)
    mutex.release()


def work2(num):
    global result
    mutex.acquire()
    for i in range(num):
        result += 1
    print('------from work2-------', result)
    mutex.release()


def main():
    print('--------begin----------', result)
    # 创建两个线程
    t1 = threading.Thread(target=work1, args=(100000000,))
    t2 = threading.Thread(target=work1, args=(100000000,))
    t1.start()
    t2.start()


if __name__ == '__main__':
    main()

我们来看一下结果:

--------begin---------- 0
------from work1------- 100000000
------from work1------- 200000000

三、死锁问题

1.单个互斥锁的死锁:

acquire()/release() 是成对出现的,互斥锁对资源锁定之后就一定要解锁,否则资源会一直处于锁定状态,其他线程无法修改;就好比上面的代码,任何一个线程没有释放资源release(),程序就会一直处于阻塞状态(在等待资源被释放),不信你可以试一试~

2.多个互斥锁的死锁:

在同时操作多个互斥锁的时候一定要格外小心,因为一不小心就容易进入死循环,假如有这样一个场景:boss让程序员一实现功能一的开发,让程序员二实现功能二的开发,功能开发完成之后一起整合代码!

# 导入线程threading模块
import threading
# 导入线程time模块
import time

# 创建互斥锁
mutex_one = threading.Lock()
mutex_two = threading.Lock()


def programmer_thread1():
    mutex_one.acquire()
    print("我是程序员1,module1开发正式开始,程序一加锁,程序二加锁")
    time.sleep(2)

    # 此时会堵塞,因为这个mutex_two已经被线程programmer_thread2抢先上锁了,等待解锁
    mutex_two.acquire()
    print("等待程序员2通知我合并代码")
    mutex_two.release()
    print('程序员2开发完了,程序员1释放第二把锁')

    mutex_one.release()
    print('程序员1开发完了,程序员1释放第一把锁')


def programmer_thread2():
    mutex_two.acquire()
    print("我是程序员2,module2开发正式开始,程序二加锁,程序一加锁")
    time.sleep(2)
    # 此时会堵塞,因为这个mutex_one已经被线程programmer_thread1抢先上锁了,等待解锁
    #mutex_two.release()
    mutex_one.acquire()
    print("等待程序员1通知我合并代码")
    mutex_one.release()
    print('程序员2释放第一把锁')
   # mutex_two.release()
    print('程序员2释放第二把锁')


def main():
    t1 = threading.Thread(target=programmer_thread1)
    t2 = threading.Thread(target=programmer_thread2)

    # 启动线程
    t1.start()
    t2.start()
    # 阻塞函数,等待线程结束
    t1.join()
    t2.join()
    # 整合代码结束
    print("整合代码结束 ")


if __name__ == "__main__":
    main()

分析下上面代码:程序员1在等程序员2通知,程序员2在等程序员1通知,两个线程都陷入阻塞中,因为两个线程都在等待对方解锁,这就是死锁!所以在开发中对于死锁的问题还是需要多多注意!

总结

到此这篇关于python多线程互斥锁与死锁问题详解的文章就介绍到这了,更多相关python互斥锁与死锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文