python 基础(四)锁 threading.Lock 死锁

python基础系列 正在持续更新中:)

锁LOCK的实例

首先我们知道线程共用资源(比如下面例子的list1变量就是可以共同访问)
上一节提到,如果处理不当,线程异步的共同访问一个变量会造成数据紊乱 所以需要人为加锁,这就是Thread.Lock
看看下面的例子是怎么防止共同访问的(避免数据紊乱)

#-*- utf-8 -*-
from threading import Lock,Thread
from time import sleep,time
lock = Lock()
list1 = [0]*10

def putR(s):
    lock.acquire()
    for i in range(len(list1)):
        list1[i] = 1
        print("put ",list1[i])
        sleep(s)
    lock.release()

def getR(s):
    lock.acquire()
    for i in range(len(list1)):
        print("get ",list1[i])
        sleep(s)
    lock.release()


if __name__ == "__main__":

    t1 = Thread(target=putR, name="aa", args=(0.5,))

    t2 = Thread(target=getR, name="aa", args=(0.5,))

    t2.start()
    t1.start()


    t1.join()
    t2.join()

结果

get  0
get  0
get  0
get  0
get  0
get  0
get  0
get  0
get  0
get  0
put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1

Process finished with exit code 0

这里start执行顺序将会影响线程的执行,为什么?
前面没有锁的时候我说谁先start都一样的 start顺序没太大意义
回答:这里不再是异步被调用,非阻塞的执行,而是同步调用,阻塞执行,谁先start就谁先做事(比如上一例中的get()),另一个只能等着(比如上一例中的put())。类比上厕所抢位置,他先抢到,厕所上锁(acquire)“有人”,我就只能等着,等他上完,锁解除(release),我才能进去。

我们来做个实验验证一下:
如果将t1 t2 start的顺序改变 那么就会变成:

put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1
get  1
get  1
get  1
get  1
get  1
get  1
get  1
get  1
get  1
get  1

Process finished with exit code 0

run 与 start

我们简单复习一下:
start启动线程之后,调用run具体执行,执行参数包括我们传过去的target = func 参数。

如果,我们想改造线程执行的内容就更改run重写)。不更改start是因为start包含线程之间的很多复杂的执行内容,我们最多可以试着添加新的内容,但是不能重写。这样添加新内容还不如更改之前人家已经给好的run呢。

死锁

这里我们自定义两个线程类ThreadA 和 ThreadB
所谓死锁,主要针对锁的嵌套问题。
线程都想获得完一个锁以后,还需要另一个锁的保护,但是只有两个锁A B。线程A获得lockA以后线程B获得lockB,这时
线程A期待lockB将会release给他
线程B期待lockA将会release给他
于是类似独木桥,
“你倒是下来啊”
“你咋不上来呢”

大致有个印象,我们来看实例:

#-*- utf-8 -*-
from threading import Thread, Lock
from time import time, sleep

lockA = Lock()
lockB = Lock()


class ThreadA(Thread):
    def run(self):  # 重载 start
        if lockA.acquire():
            print(self.name + ' get Lock A')
            sleep(0.1)
            if lockB.acquire():
                print(self.name + ' get Lock A+B')
                sleep(0.1)
                lockB.release()
            lockA.release()


class ThreadB(Thread):
    def run(self):  # 重载 start
        if lockB.acquire():
            print(self.name + ' get Lock B')
            sleep(0.1)
            if lockA.acquire():
                print(self.name + ' get Lock B+A')
                sleep(0.1)
                lockA.release()
            lockB.release()


if __name__ == '__main__':
    t1 = ThreadA()
    t2 = ThreadB()

    t1.start()
    t2.start()

    t1.join()
    t2.join()

ThreadA先拿到LockA是没问题的,谁先抢到厕所谁就赢了:)

执行结果:
在这里插入图片描述
明显,ThreadA与ThreadB互不相让,各自为政,各拿一个锁期待另一个人退一步,然后僵持在这里。
一般解决方法有两个,一个是设置timeout,将代码的acquire属性用起来:
在这里插入图片描述
结果如下:
在这里插入图片描述
因为ThreadA先执行,都是timeout=2 于是A先释放了lockA,B就等到了,拿到了双杀 双锁

再看看另一个例子:

#-*- utf-8 -*-
from threading import Thread, Lock
from time import time, sleep

lockA = Lock()
lockB = Lock()


class ThreadA(Thread):
    def run(self):  # 重载 start
        while True:
            if lockA.acquire(timeout=2):
                print(self.name + ' get Lock A')
                sleep(0.1)
                if lockB.acquire(timeout=2):
                    print(self.name + ' get Lock A+B')
                    sleep(0.1)
                    lockB.release()
                lockA.release()


class ThreadB(Thread):
    def run(self):  # 重载 start
        while True:
            if lockB.acquire(timeout=2):
                print(self.name + ' get Lock B')
                sleep(0.1)
                if lockA.acquire(timeout=2):
                    print(self.name + ' get Lock B+A')
                    sleep(0.1)
                    lockA.release()
                lockB.release()


if __name__ == '__main__':
    t1 = ThreadA()
    t2 = ThreadB()

    t1.start()
    t2.start()

    t1.join()
    t2.join()

结果是这样的:

Thread-1 get Lock A
Thread-2 get Lock B
Thread-2 get Lock B
Thread-1 get Lock A

Thread-1 get Lock A
Thread-2 get Lock B
Thread-2 get Lock B
Thread-1 get Lock A
Thread-1 get Lock A
Thread-2 get Lock B

思考一下为什么会这样:)

下一站:python 基础(五)协程 —— 微线程 greenlet gevent

发布了28 篇原创文章 · 获赞 24 · 访问量 8931

猜你喜欢

转载自blog.csdn.net/weixin_43178828/article/details/104086899