Python之多线程(四)多线程中的锁实现

背景

多线程并发问题

账户余额示例代码:

# 我的银行账户
import threading
import time

balance = 0


def change_it(n):
    """ 改变我的余额 """
    global balance
    balance = balance + n
    time.sleep(2)
    balance = balance - n
    time.sleep(1)
    print('-N----->{0}; balance:{1}'.format(n, balance))


class ChageBalnceThread(threading.Thread):
    """
    改变银行余额的线程
    """
    def __init__(self, num, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.num = num

    def run(self):
        for i in range(10000):
            change_it(self.num)


if __name__ == '__main__':
    t1 = ChageBalnceThread(5)
    t2 = ChageBalnceThread(8)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print('the last:{0}'.format(balance))

测试以上代码的结果会发现一个问题最终结果不一定为零

  • 这是为什么?
  • 因为线程是并发执行的不是交替执行的,

所以当两个线程去跑的时候,第一个线程传进去5、第二个是8;在没有锁的情况下可能出现,第一个线程还没结束,第二个线程就进去把这个参数带进去了,导致可能和第一个参数相互加减问题。

  • 当加上锁之后,会将后面的任务进行阻塞,直到正在执行的线程执行完毕。

有锁的情况就是,当地一个线程进去后,第二个线程就进不去,必须等着第一个线程释放完,第二个线程才能进去。

 

threading.Lock()

描述

锁原语对象(和thread模块中的锁一样)

用法

Lock() 处于“锁定”或“解锁”两种状态之一。它是在解锁状态下创建的。

它有两种基本方法,acquire()release()

解锁状态后,acquire()将状态更改为锁定并立即返回。

当状态锁定时, acquire()阻塞直到release()另一个线程中的调用将其更改为解锁,然后该acquire()调用将其重置为锁定并返回。

release()方法只能在锁定状态下调用;它将状态更改为解锁并立即返回。

如果试图释放解锁的锁, RuntimeError将引发a。

重点

Lock()当已经处于锁定(acquire())状态时,再重复锁定,将产生死锁。

实例代码:

# 我的银行账户
import threading
import time

# 获得一把锁
my_lock = threading.Lock()
your_lock = threading.RLock()

# 初始金额为零
balance = 0


def change_it(n):
    """ 改变我的余额 """
    global balance

    try:
        print('start lock')
        # 添加锁
        my_lock.acquire()
        print('locked one ')
        # 资源已经被锁住了,不能重复锁定,产生死锁
        my_lock.acquire()
        print('locked two')
        balance = balance + n
        time.sleep(2)
        balance = balance - n
        time.sleep(1)
        print('-N----->{0}; balance:{1}'.format(n, balance))
    finally:
        # 释放掉锁
        my_lock.release()
        my_lock.release()


class ChageBalnceThread(threading.Thread):
    """
    改变银行余额的线程
    """
    def __init__(self, num, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.num = num

    def run(self):
        for i in range(100):
            change_it(self.num)


if __name__ == '__main__':
    t1 = ChageBalnceThread(5)
    t2 = ChageBalnceThread(8)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print('the last:{0}'.format(balance))

 

 

threading.Rlock()

描述

可重入锁是一个同步原语,可以由同一线程多次获取。在内部,除Lock()使用的锁定/解锁状态外,它还使用“拥有线程”和“递归级别”的概念。

在锁定状态下,某些线程拥有该锁;在解锁状态下,没有线程拥有它。

此类实现可重入锁定对象。可重入锁必须由获取它的线程释放。

一旦线程获得了可重入锁,同一线程就可以再次获得它而不会阻塞;线程必须在每次获取它后释放一次。(使单一线程可以(再次)获得已持有的锁(递归锁)。)

用法

要锁定该锁,线程将调用其acquire()方法。

一旦线程拥有锁,它将返回。

要解锁锁,线程将调用其release()方法。

acquire()/ release()呼叫对可以嵌套;只有最后一个release()release()最外面的一对)将锁重置为解锁状态,并允许另一个被阻塞的线程acquire()继续进行。

重点

方式一:使用try…finally… RLock()锁了几次就要释放几次锁。

方式二:使用with RLock(),不用释放锁。

 

一、实例代码:

# 方式一:使用try...finally... 
# 我的银行账户
import threading
import time

# 获得一把锁
my_lock = threading.Lock()
your_lock = threading.RLock()

# 初始金额为零
balance = 0


def change_it(n):
    """ 改变我的余额 """
    global balance

    try:
        print('start lock')
        # 添加锁
        your_lock.acquire()
        print('locked one ')
        your_lock.acquire()
        print('locked two')
        balance = balance + n
        time.sleep(2)
        balance = balance - n
        time.sleep(1)
        print('-N----->{0}; balance:{1}'.format(n, balance))
    finally:
        # 释放掉锁
        your_lock.release()
        your_lock.release()


class ChageBalnceThread(threading.Thread):
    """
    改变银行余额的线程
    """
    def __init__(self, num, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.num = num

    def run(self):
        for i in range(100):
            change_it(self.num)


if __name__ == '__main__':
    t1 = ChageBalnceThread(5)
    t2 = ChageBalnceThread(8)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print('the last:{0}'.format(balance))

二、实例代码:

# 我的银行账户
import threading
import time

# 获得一把锁
my_lock = threading.Lock()
your_lock = threading.RLock()

# 初始金额为零
balance = 0


def change_it(n):
    """ 改变我的余额 """
    global balance

    # 方式二,使用with
    with your_lock:
        balance = balance + n
        time.sleep(2)
        balance = balance - n
        time.sleep(1)
        print('-N----->{0}; balance:{1}'.format(n, balance))


class ChageBalnceThread(threading.Thread):
    """
    改变银行余额的线程
    """
    def __init__(self, num, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.num = num

    def run(self):
        for i in range(100):
            change_it(self.num)


if __name__ == '__main__':
    t1 = ChageBalnceThread(5)
    t2 = ChageBalnceThread(8)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print('the last:{0}'.format(balance))

 Condition()

Python3

Python之多线程(三)实现一个线程

2019-11-9 6:53:40

Python3

进程

2019-11-16 2:36:13

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索