亲宝软件园·资讯

展开

python互斥锁与死锁

陈小c 人气:0

一、多线程共享全局变量

代码实现的功能:

创建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通知,两个线程都陷入阻塞中,因为两个线程都在等待对方解锁,这就是死锁!所以在开发中对于死锁的问题还是需要多多注意!

总结

加载全部内容

相关教程
猜你喜欢
用户评论