Python(GIL,同步锁,死锁和递归锁)

本文详细探讨了Python全局解释器锁(GIL)的工作原理,以及如何使用同步原语如锁来防止竞态条件。同时,我们还分析了死锁的概念及其在多线程编程中的影响,并介绍了递归锁在解决特定同步问题中的应用。
摘要由CSDN通过智能技术生成
#并发:是指系统具有处理多个任务(动作)的能力
#并行:是指系统具有同时处理多个任务(动作)的能力
#同步:当进程执行到一个IO(等待外部数据,或者下载上传等)的时候 -----------等待:这个就是同步
#异步:当进程执行到一个IO(等待外部数据,或者下载上传等)的时候 -----------不等待而去执行其他操作,到数据接收成功再来执行:这个就是异步

#同步锁
import threading
import time
def foo():
    global num #在每个线程中都能获取这个全局变量
    num -= 1
def foo1():
    global num #在每个线程中都能获取这个全局变量
    lock.acquire()#加锁
    temp = num
    time.sleep(0.1)
    num = temp-1
    lock.release()#释放锁
num = 10
lock = threading.Lock()#创建一个同步锁对象
l = []
for i in range(10):
    t = threading.Thread(target=foo1)
    t.start()
    l.append(t)
for t in l:
    t.join()
print(num)#当用foo创建了10个线程,num的最终结果得到的是0,用foo1创建了10个线程,num的最终结果得到的是9,或者8等等为什么呢
#这个就是同步锁了,我们知道同一进程下所有线程共享进程所具有的资源,在foo函数中得到的结果是0,是因为内存运算速度非常快,
# 所以每个线程运行时函数都已经拿到最新的num的值了;而在foo1中,我们让系统认为的阻塞了一下,这时线程在运行时,就会导致很多线
# 程拿到的都是初始的那个num,这样就会导致运算出来的结果是不对的,那我们怎么解决这个问题呢????
# 我们可以每个线程之后都join(),但是这样的话整段代码都失去了并发的意义
#加同步锁
#加上同步锁之后我们就解决的这个问题,加上同步锁之后,我们在加锁的代码部分同一时间只能运行一个线程,
# 这样我们的到的数据就是每次运行结束之后的最新数据了,但是有一个问题就是这样的话,在加锁的部位我们的并发就失去了意义,不过比join要好一点
#线程死锁和递归锁
import threading,time
class myThread(threading.Thread):
    def doA(self):
        #lockA.acquire()
        lock.acquire()

        print(self.name,'gotlockA',time.ctime())
        time.sleep(3)
        lock.acquire()
        #lockB.acquire()
        print(self.name, 'gotlockB', time.ctime())
        lock.release()
        lock.release()
        # lockB.release()
        # lockA.release()
    def doB(self):
        lock.acquire()
        #lockB.acquire()
        print(self.name, 'gotlockB', time.ctime())
        time.sleep(3)
        lock.acquire()
        #lockA.acquire()
        print(self.name, 'gotlockA', time.ctime())
        # lockA.release()
        # lockB.release()
        lock.release()
        lock.release()
    def run(self):
        self.doA()
        self.doB()
if __name__ == '__main__':
    # lockA = threading.Lock()
    # lockB = threading.Lock()
    lock = threading.RLock()#这个就是递归锁
    l = []
    for i in range(5):
        l.append(myThread())
    for t in l:
        t.start()
    for t in l:
        t.join()
    print('ending----------')
    # 打印结果
    # Thread-1 gotlockA Fri Dec  4 11:30:55 2020
    # Thread-1 gotlockB Fri Dec  4 11:30:58 2020
    # Thread-1 gotlockB Fri Dec  4 11:30:58 2020
    # Thread-2 gotlockA Fri Dec  4 11:30:58 2020
    #我们发现运行之后,系统打印这些结果之后就不在打印了,并没有走到最后一步打印出ending----------为什么呢。。。。。
    #这是因为造成了死锁,怎么造成的,且听我一一道来
    # 线程1走完doA之后,开始走doB了,此时线程2开始走doA了,线程1这时获得了B锁,接着往下走准备获得A锁,而此时线程2已经获得了A锁,
    # 然后线程1等着A锁释来取得A锁,而线程2还没有到释放A锁的时候,此时线程2该获得B锁了,但是线程1拿着B锁,也没有到释放的时候,
    # 这时两个线程都释放不了各自的锁,而且都拿着对方需要的锁,就这死锁就出现了,程序阻塞。怎么解决??
    #加递归锁
    #把下面这两个锁改为lock= threading.RLock()
    # lockA = threading.Lock()
    # lockB = threading.Lock()
    #其实递归锁的原理核心是一个count计数,就是当这个锁大于0时说明有线程在用,而其他线程就用不了,当这个锁计数是0时,其他线程才能用
'''
    # 1 一个程序至少有一个进程,一个进程至少有一个线程.(进程可以理解成线程的容器)
    # 2 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
    # 3 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和
    #   程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
    # 4 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调 度的一个独立单位.
    #   线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程
    #   自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)但是
    #   它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
    #   一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行

'''
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:#这个加锁去锁的方式和with open(file,'r')差不多的用法,运行完自己释放锁
            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)
'''
def add():
    sum=0

    for i in range(10000):
        sum+=i
    print("sum",sum)


def mul():
    sum2=1
    for i in range(1,100000):
        sum2*=i
    print("sum2",sum2)

import threading,time


start=time.time()

t1=threading.Thread(target=add)
t2=threading.Thread(target=mul)

l=[]
l.append(t1)
l.append(t2)


for t in l:
    t.start()



for t in l:
    t.join()

# add()
# mul()

print("cost time %s"%(time.time()-start))
#add() mul()串行执行时时间是cost time 28.294472455978394
#使用线程并发运行时的时间是cost time 29.023944854736328
#为什么并发的时间比串行的时间长
#python 在跑并发时,都是一个核跑的
#问题:多核没利用上?
#GIL 全局解释锁
#无论你启用多少个线程,你有多少个CPU,Python在执行的时候都会淡定的在同一时刻只允许一个线程运行
#因为有GIL,所以。同一时刻,只有一个线程被执行,而在并行时线程在快速的切换争夺CPU,所以在串行的时候比并行的快
#解决方法
#可以把线程放到进程里面,这样就可以解决这个问题了
#多进程+协程
#第一个例子为什么会实现并发时间少
#原因是第一个例子是IO了,所以会并发时间少,sleep()等同于IO操作
#任务:IO密集型,计算密集型
#IO密集型使用并发会大量缩减时间,计算密集型使用并发会比串行执行时间会更长
#对于IO密集型的任务:Python的多线程是有意义的
#对于计算密集型的任务,Python的多线程就不推荐了,Python就不适用了,可以使用进程+协程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZhaoXuWen23

你的鼓励是我的动力,持续更新中

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值