Python学习第47天(递归锁、同步对象、信号量)

  今天三个部分的知识、递归锁(解决同步锁造成的锁死现象)、同步对象(类似一个锁,使两个线程之间进行同步)、信号量(控制线程数量的一个锁),悄悄的说一下,算上昨天的同步锁,虽然已经讲了四种锁了,但是据说总共是有五种锁的,最后一个我还不知道是个啥。

  一、递归锁

  昨天引入了同步锁的概念,但是因为同步锁的引入,又发生了一个问题,就是两个锁来回锁,导致程序最终被锁死,你等我我等你,谁都没法运行了,先看一下锁死的情况吧,情况如下:

import threading,time

class myThread(threading.Thread):
    def doA(self):
        lockA.acquire()
        print(self.name,"gotlockA",time.ctime())
        time.sleep(3)
        lockB.acquire()
        print(self.name,"gotlockB",time.ctime())
        lockB.release()
        lockA.release()

    def doB(self):
        lockB.acquire()
        print(self.name,"gotlockB",time.ctime())
        time.sleep(2)
        lockA.acquire()
        print(self.name,"gotlockA",time.ctime())
        lockA.release()
        lockB.release()

    def run(self):
        self.doA()
        self.doB()
if __name__=="__main__":

    lockA=threading.Lock()
    lockB=threading.Lock()
    threads=[]
    for i in range(5):
        threads.append(myThread())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

  大致说一下上面锁死的情况,还是画一个图吧,公文写的太多,现在一看到文字描述就会感觉自己词穷了:

  当线程1的doA操作完成后,两把锁均被打开,此时就有可能切换到线程2上,此时第一个线程获得了B锁,而第二个线程几乎同时获得了A锁,然后尴尬的事情发生了,线程1需要的A锁被线程2抢了,线程2需要的B锁被线程1抢了,然后都不愿意,最后就锁死了。

  所以为了解决上面的问题,引入了递归锁:

  rlock = threading.rLock( )

  为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

  把上面刚才有问题的AB锁全部替换成为rlock,就可以解决这个问题了,然后会发现,他会doA执行完全部执行另一个线程的doB或者下一个doA,不会发生锁死

  引入一个案例吧!  

import time
import threading

class Account:
    def __init__(self, _id, balance):
        self.id = _id
        self.balance = balance
        self.lock = threading.RLock()

    def withdraw(self, amount):
        with self.lock:
            self.balance -= amount

    def deposit(self, amount):
        with self.lock:
            self.balance += amount

    def drawcash(self, amount):#lock.acquire中嵌套lock.acquire的场景

        with self.lock:
            interest=0.05
            count=amount+amount*interest

            self.withdraw(count)

def transfer(_from, to, amount):
    #锁不可以加在这里 因为其他的其它线程执行的其它方法在不加锁的情况下数据同样是不安全的
     _from.withdraw(amount)

     to.deposit(amount)

alex = Account(‘alex‘,1000)
yuan = Account(‘yuan‘,1000)

t1=threading.Thread(target = transfer, args = (alex,yuan, 100))
t1.start()

t2=threading.Thread(target = transfer, args = (yuan,alex, 200))
t2.start()

t1.join()
t2.join()

print(‘>>>‘,alex.balance)
print(‘>>>‘,yuan.balance)

  二、同步对象(Event)

  啥是同步啊,昨天说的,就想io输入,我等着你就是同步,异步就是我先走了,等你搞好了再回来接你

  直接上案例吧,下面这个就是使用Event实现的同步,员工等着老板发话。

import threading,time
class Boss(threading.Thread):
    def run(self):
        print("BOSS:今晚大家都要加班到22:00。")
        print(event.isSet())
        event.set()
        time.sleep(5)
        print("BOSS:<22:00>可以下班了。")
        print(event.isSet())
        event.set()
class Worker(threading.Thread):
    def run(self):
        event.wait()
        print("Worker:哎……命苦啊!")
        time.sleep(1)
        event.clear()
        event.wait()
        print("Worker:OhYeah!")
if __name__=="__main__":
    event=threading.Event()
    threads=[]
    for i in range(5):
        threads.append(Worker())
    threads.append(Boss())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

  通过event = therading.Event( )  然后通过event.wait()等待一个状态

  此时event.isSet的状态是False,然后上面这个wait就会一直等着

  然后发生了event.set(),通过set,是原来的False变成了True

  这个时候event.wait()就可以开始正常执行了

  event.clear()能够使event的状态重新回到False

  这里应该注意的点: event的状态是在各个线程内都是有效地,状态通用

  三、信号量(Semaphore)

  信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。

  计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)

  BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。

  这个内容好像没怎么重点说,可能在后期具体使用的时候才会具体用到,直接看案例:

import threading,time
class myThread(threading.Thread):
    def run(self):
        if semaphore.acquire():
            print(self.name)
            time.sleep(5)
            semaphore.release()
if __name__=="__main__":
    semaphore=threading.Semaphore(5)
    thrs=[]
    for i in range(100):
        thrs.append(myThread())
    for t in thrs:
        t.start()

  这样每次都只会蹦出5个线程

今天打算努力回忆一下之前的知识,但是发现真的是忘的精光,最近脑子也是极其的不好使,周末开始复习,不放风筝。

原文地址:https://www.cnblogs.com/xiaoyaotx/p/12670603.html

时间: 2024-11-07 14:39:04

Python学习第47天(递归锁、同步对象、信号量)的相关文章

Python并发编程05/ 死锁/递归锁/信号量/GIL锁/进程池/线程池

目录 Python并发编程05/ 死锁/递归锁/信号量/GIL锁/进程池/线程池 1.昨日回顾 2.死锁现象与递归锁 2.1死锁现象 2.2递归锁 3.信号量 4.GIL全局解释器锁 4.1背景 4.2为什么加锁 5.GIL与Lock锁的区别 6.验证计算密集型IO密集型的效率 6.1 IO密集型 6.2 计算密集型 7.多线程实现socket通信 7.1服务端 7.2客户端 8.进程池,线程池 Python并发编程05/ 死锁/递归锁/信号量/GIL锁/进程池/线程池 1.昨日回顾 #生产者消

python学习第37天GIL锁、死锁现象与递归锁、信号量、Event时间、线程queue

一.GIL锁 1. 什么是GIL全局解释器锁 定义: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython's memory management is not thread-safe

python开发线程:死锁和递归锁&amp;信号量&amp;定时器&amp;线程queue&amp;事件evevt

一 死锁现象与递归锁 进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁 from threading import Thread,Lock import time mutexA=Lock() mutexB=Lock() class MyThread(Thread):

python多线程编程(2): 使用互斥锁同步线程

上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的代码: # encoding: UTF-8import threadingimport time class MyThread(threading.Thread): def run(self): global num time.sleep(1) num = num+1 msg = self.name+' set

Python学习笔记12:标准库之对象序列化(pickle包,cPickle包)

计算机的内存中存储的是二进制的序列. 我们能够直接将某个对象所相应位置的数据抓取下来,转换成文本流 (这个过程叫做serialize),然后将文本流存入到文件里. 因为Python在创建对象时,要參考对象的类定义,所以当我们从文本中读取对象时,必须在手边要有该对象的类定义,才干懂得怎样去重建这一对象. 从文件读取时,对于Python的内建(built-in)对象 (比方说整数.词典.表等等),因为其类定义已经加载内存,所以不须要我们再在程序中定义类. 但对于用户自行定义的对象,就必需要先定义类,

PYTHON学习0029:函数---递归----2019-6-20

递归特性: 原文地址:https://blog.51cto.com/13543767/2411753

Python学习笔记12:标准库之对象序列化

计算机的内存中存储的是二进制的序列. 我们可以直接将某个对象所对应位置的数据抓取下来,转换成文本流 (这个过程叫做serialize),然后将文本流存入到文件中. 由于Python在创建对象时,要参考对象的类定义,所以当我们从文本中读取对象时,必须在手边要有该对象的类定义,才能懂得如何去重建这一对象. 从文件读取时,对于Python的内建(built-in)对象 (比如说整数.词典.表等等),由于其类定义已经载入内存,所以不需要我们再在程序中定义类. 但对于用户自行定义的对象,就必须要先定义类,

《python学习手册》第34章 异常对象

基于字符串的异常 python在2.6之前可以使用字符串来定义异常,并且是通过对象标识符来匹配的(即通过is 而不是==) myexc = "My excetion string" try: raise myexc except myexc: print('caught') 基于类的异常 字符串定义的异常非常简单,但是并不容易维护.使用类定义的异常通过超类关系进行匹配,只要except列举出来的异常的类或者任何超类名,引发的异常都会匹配到.此外,类的异常还支持异常状态信息,可以让异常参

Python学习【第24篇】:死锁,递归锁,信号量,Event事件,线程Queue

python并发编程之多线程2------------死锁与递归锁,信号量等 一.死锁现象与递归锁 进程也是有死锁的 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用, 它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程, 如下就是死锁 1 死锁------------------- 2 from threading import Thread,Lock,RLock 3 import tim