python中--锁-死锁-递归锁-线程锁--生产者消费者模型

Lock 互斥锁

进程之间数据安全的问题

把要保护的数据锁起来,每次只有一个人能访问

注意:

1.锁不要轻易的使用,容易造成死锁现象(我们写代码一般不会用大,都是用内部封装好的)

2.锁只在处理数据的部分加,来保证数据安全(只有在争抢数据的换届加锁处理即可)

  • lock.acquire() 取钥匙
  • lock.release() 还钥匙
#  @时间 : 2020/6/29 14:41
#  @作者 : 子清

from multiprocessing import Process, Lock
import json
import time

def show_ticket(i):
    with open('ticket', encoding='utf-8', mode='r') as f:
        ret = json.load(f)
        time.sleep(0.5)
        print(f'{i}:还剩{ret["count"]}张票')

def buy_ticket(i):
    with open('ticket',encoding='utf-8',mode='r') as f:
        ret = json.load(f)
    if ret['count'] > 0:
        ret['count'] -= 1
        print(f'{i}买票成功')
    time.sleep(0.2)
    with open('ticket',encoding='utf-8',mode='w') as f:
        json.dump(ret,f)

def get_ticket(i,lock):
    show_ticket(i)
    lock.acquire()  
    buy_ticket(i)
    lock.release()

if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        p1 = Process(target=get_ticket, args=(i,lock))
        p1.start()

其中的取钥匙和还钥匙动作可以简写 with lock: 尽量用这个

代替acquire和release 并且在此基础上做了一些异常处理,保证即便一个进程的代码出错退出了,也会归还钥匙

def get_ticket(i,lock):
    show_ticket(i)
    
    lock.acquire()
    buy_ticket(i)
    lock.release()
    
# ------等同于-------

def get_ticket(i,lock):
    show_ticket(i)
    with lock:
        buy_ticket(i)
from multiprocessing import Lock  # 互斥锁,不能再同一个进程中连续acquire多次
lock = Lock()
lock.acquire()
print(1)
lock.acquire()
print(2)

# --------------解决办法,把acquire和release对应起来,一次取一次还

from multiprocessing import Lock  # 互斥锁,不能再同一个进程中连续acquire多次
lock = Lock()
lock.acquire()  # 取
print(1)
lock.release()  # 还
lock.acquire()  # 取
print(2)
lock.release()  # 还

线程锁

+= -= *= /= while if 数据不安全 +和赋值是分开的两个操作

append pop 数据安全, 列表中的方法或者字典中的方法去操作全局变量的时候 数据安全的

线程之间也存在数据不安全

  • 加锁解决

python中--锁-死锁-递归锁-线程锁--生产者消费者模型


from threading import Thread,Lock
n = 0
def foo(lock):
    for i in range(100000):
        global n
        with lock:
            n += 1

def son(lock):
    for i in range(100000):
        global n
        with lock:
            n -= 1

if __name__ == '__main__':
    t_l = []
    lock = Lock()
    for i in range(2):
        t = Thread(target=foo,args=(lock,))
        t.start()
        t1 = Thread(target=son,args=(lock,))
        t1.start()
        t_l.append(t)
        t_l.append(t1)
    for i in t_l:
        i.join()
    print(n)

不要操作全局变量, 不要在类里操作静态变量

递归锁

python中--锁-死锁-递归锁-线程锁--生产者消费者模型


from threading import Lock,RLock

Lock互斥锁 RLock递归锁(recursion)

递归锁相当于万能钥匙,锁里面还有锁也能进(递归),同样的你进了多少锁(acquire),你就要出来多少次(release)。

  • 用法一样,都是acquire()和release()
from threading import Lock,RLock

l = Lock()
l.acquire()
# l.acquire()  # 互斥锁不能连续acquire多次
print('要锁住的代码')
l.release()

rl = RLock()
rl.acquire()  # 递归锁在同一个线程中可以被acquire多次
rl.acquire()
rl.acquire()
print('要锁着的代码')
rl.release()
from threading import Lock,RLock,Thread

def func(i,lock):
    lock.acquire()
    print(f'{i}--start...')
    lock.release()
    print(f'{i}--end...')

if __name__ == '__main__':
    lock = Lock()
    for i in range(5):
        t = Thread(target=func, args=(i, lock))
        t.start()
#-------------------------------------
from threading import Lock,RLock,Thread

def func(i,lock):
    lock.acquire()
    lock.acquire()
    lock.acquire()
    print(f'{i}--start...')
    lock.release()
    lock.release()
    lock.release()
    print(f'{i}--end...')

if __name__ == '__main__':
    lock = RLock()
    for i in range(5):
        t = Thread(target=func, args=(i, lock))
        t.start()

死锁

互斥锁效率高,递归锁效率高低

递归锁 效率低 但是解决死锁现象有奇效

互斥锁 效率高 但是多把锁容易出现死锁现象


死锁现象是怎么产生的?

  • 多把(互斥/递归)锁 并且在多个线程中 交叉使用
from threading import Thread, Lock, RLock
import time
noodle_lock = Lock()  # 互斥锁
fork_lock = Lock()  # 互斥锁
#  实例化了两把锁

def eat(name):
    noodle_lock.acquire()
    print(name, '抢到了面')
    fork_lock.acquire()
    print(name, '抢到了叉子')

    print(name, '吃面')
    time.sleep(1)

    noodle_lock.release()
    print(name, '放回了面')
    fork_lock.release()
    print(name, '放回了叉子')

def eat2(name):
    fork_lock.acquire()
    print(name, '抢到了叉子')
    noodle_lock.acquire()
    print(name, '抢到了面')

    print(name, '吃面')
    time.sleep(1)

    fork_lock.release()
    print(name, '放回了叉子')
    noodle_lock.release()
    print(name, '放回了面')

t = Thread(target=eat, args=('alex',)).start()
t1 = Thread(target=eat2, args=('wusir',)).start()
t2 = Thread(target=eat, args=('taibai',)).start()
t3 = Thread(target=eat2, args=('egon',)).start()
  • 如果是互斥锁,出现了死锁现象,最快速的解决方案把所有的互斥锁都改成同一把递归锁(注意:是同一把!!)
    • 但是程序效率会降低
from threading import Thread, Lock, RLock
import time
fork_lock = noodle_lock = RLock()  # 这里换成RLock递归锁,并且这两把锁是同一把锁,只实例化了一把锁

# ----------------下面都没改,主要是上面-----------

def eat(name):
    noodle_lock.acquire()
    print(name, '抢到了面')
    fork_lock.acquire()
    print(name, '抢到了叉子')

    print(name, '吃面')
    time.sleep(1)

    noodle_lock.release()
    print(name, '放回了面')
    fork_lock.release()
    print(name, '放回了叉子')

def eat2(name):
    fork_lock.acquire()
    print(name, '抢到了叉子')
    noodle_lock.acquire()
    print(name, '抢到了面')

    print(name, '吃面')
    time.sleep(1)

    fork_lock.release()
    print(name, '放回了叉子')
    noodle_lock.release()
    print(name, '放回了面')

t = Thread(target=eat, args=('alex',)).start()
t1 = Thread(target=eat2, args=('wusir',)).start()
t2 = Thread(target=eat, args=('taibai',)).start()
t3 = Thread(target=eat2, args=('egon',)).start()
  • 或者把两把锁改成一把(互斥锁和递归锁都行)
from threading import Thread, Lock, RLock
import time
fork_noodle_lock = Lock()  # 这里用的互斥锁,递归锁也是可以的


def eat(name):
    fork_noodle_lock.acquire()  # 两把改成一把
    print(name, '抢到了面')
    print(name, '抢到了叉子')

    print(name, '吃面')
    time.sleep(0.1)

    fork_noodle_lock.release()
    print(name, '放回了面')
    print(name, '放回了叉子')

def eat2(name):
    fork_noodle_lock.acquire()
    print(name, '抢到了叉子')
    print(name, '抢到了面')

    print(name, '吃面')
    time.sleep(1)

    fork_noodle_lock.release()
    print(name, '放回了叉子')
    print(name, '放回了面')

t = Thread(target=eat, args=('alex',)).start()
t1 = Thread(target=eat2, args=('wusir',)).start()
t2 = Thread(target=eat, args=('taibai',)).start()
t3 = Thread(target=eat2, args=('egon',)).start()

生产者消费者模型

consumer消费者,producer生产者

本质:就是让生产数据和消费数据的效率达到平衡并且最大化的效率

from multiprocessing import Process, Queue
import time, random


def producer(q, name, food):  # 生产者:通常在放数据之前需要先通过某些代码来获取数据
    for i in range(10):
        foodi = f'{food}{i}'
        time.sleep(random.random())
        q.put(foodi)
        print(f'{name}生产了{foodi}')


def consumer(q, name):  # 消费者:通常取到数据之后还要进行某些操作
    while 1:
        food = q.get()
        if food:
            print(f'{name}吃了{food}')
        else:
            break

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer, args=(q, 'ziqing', '樱桃'))
    p2 = Process(target=producer, args=(q, 'zxc', '草莓'))

    c1 = Process(target=consumer, args=(q, '太白'))
    c2 = Process(target=consumer, args=(q, 'alex'))

    p1.start()
    p2.start()
    c1.start()
    c2.start()
    p1.join()
    p2.join()
    q.put(None)
    q.put(None)
上一篇:C语言基础学习基本数据类型-变量的输出与输入


下一篇:126 python高级 - 同步应用