zoukankan      html  css  js  c++  java
  • 044.Python线程的数据安全


    线程的数据安全

    1 数据混乱现象

    复制代码
    from threading import Thread,Lock
    num = 0
    lst = []
    
    
    def func1():
            global num
            for i in range(100000):
                    num -= 1
    
    def func2():
            global num
            for i in range(100000):
                    num += 1
    for i  in range (10):
    # 启动线程1
            t1 = Thread(target=func1)
            t1.start()
    # 启动线程2
            t2 = Thread(target=func2)
            t2.start()
            lst.append(t1)
            lst.append(t2)
    
    for i in lst:
            i.join()
    print("主线程执行结束")
    print(num)
    复制代码

    执行

    由于共享同一份,会出现数据混乱,如下结果

    复制代码
    [root@node10 python]# python3 test.py
    主线程执行结束
    173624
    [root@node10 python]# python3 test.py
    主线程执行结束
    131875
    [root@node10 python]# python3 test.py
    主线程执行结束
    -4279
    [root@node10 python]# python3 test.py
    主线程执行结束
    -93587
    [root@node10 python]# python3 test.py
    主线程执行结束
    145643
    复制代码

    这是因为线程是并发执行,同同一时间会有多个线程拿到同一个数据进行计算,然后再放回去,导致一个数字被多个线程修改,结果不准确

    2 引入锁机制

    复制代码
    from threading import Thread,Lock
    num = 0
    lst = []
    
    
    def func1(lock):
            global num
            for i in range(100000):
                    #上锁修改数据
                    lock.acquire()
                    num -= 1
                    #解锁释放
                    lock.release()
    
    def func2(lock):
            global num
            for i in range(100000):
                    #使用with,自动上锁和解锁
                    with lock:
                            num += 1
    lock = Lock()
    for i  in range (10):
    # 启动线程1
            t1 = Thread(target=func1,args=(lock,))
            t1.start()
    # 启动线程2
            t2 = Thread(target=func2,args=(lock,))
            t2.start()
            lst.append(t1)
            lst.append(t2)
    
    for i in lst:
            i.join()
    print("主线程执行结束")
    print(num)
    复制代码

    执行

    [root@node10 python]# python3 test.py
    主线程执行结束
    0

    结果正确,但是消耗的时间毕竟长,但是可以换取数据正确

    3 信号量

    复制代码
    import time,random,os
    
    def func(i,sem):
            time.sleep(random.uniform(0.1,1))
            with sem:
                    print (i)
                    print (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
                    time.sleep(random.uniform(3,6))
    
    
    sem = Semaphore(5)
    for i in range (20):
            Thread(target = func,args=(i,sem)).start()
    复制代码

    执行

    复制代码
    [root@node10 python]# python3 test.py
    3
    2020-02-23 03:55:24
    5
    2020-02-23 03:55:24
    14
    2020-02-23 03:55:24
    18
    2020-02-23 03:55:24
    6
    2020-02-23 03:55:24
    0
    2020-02-23 03:55:27
    2
    2020-02-23 03:55:29
    15
    2020-02-23 03:55:29
    1
    2020-02-23 03:55:29
    19
    2020-02-23 03:55:30
    12
    2020-02-23 03:55:32
    4
    2020-02-23 03:55:32
    17
    2020-02-23 03:55:34
    16
    2020-02-23 03:55:34
    13
    2020-02-23 03:55:35
    7
    2020-02-23 03:55:36
    10
    2020-02-23 03:55:36
    8
    2020-02-23 03:55:39
    9
    2020-02-23 03:55:39
    11
    2020-02-23 03:55:40
    复制代码

    信号量就相当于同时可以上多把锁

    死锁,递归锁,互斥锁

    4 死锁现象

    模拟一个俱乐部玩枪,两把枪,两盒子弹,四个人同时抢,只有同时抢到子弹和枪的人,才能玩枪

    初步逻辑

    复制代码
    from threading import Thread,Lock
    import time
    #首先创建两把锁
    gun = Lock()
    bullet = Lock()
    #定义第一种,先抢到枪,再抢到子弹
    def play1(name):
            #抢到枪,上锁
            gun.acquire()
            print("%s拿到枪"%(name))
            #抢到子弹上锁
            bullet.acquire()
            print ("%s抢到子弹"%(name))
            print ("开枪玩一会")
    
            time.sleep(0.5)
            #放下枪,解锁
            gun.release()
            print("%s放下枪"%(name))
            #放下子弹,解锁
            bullet.release()
            print("%s放下子弹"%(name))
    
    #定义第二种,先抢到子弹,再抢到枪
    def play2(name):
            #抢到子弹上锁
            bullet.acquire()
            print ("%s抢到子弹"%(name))
             #抢到枪,上锁
            gun.acquire()
            print("%s拿到枪"%(name))
            print ("开枪玩一会")
            time.sleep(0.5)
            #放下子弹,解锁
            bullet.release()
            print("%s放下子弹"%(name))
            #放下枪,解锁
            gun.release()
            print("%s放下枪"%(name))
    
    
    name_lst1 = ["John","Jim"]
    name_lst2 = ["Tom","Jerry"]
    for name  in  name_lst1:
            Thread(target=play1,args=(name,)).start()
    for name  in  name_lst2:
            Thread(target=play2,args=(name,)).start()
    复制代码

    这种情况就会有死锁现象

    多次执行,结果如下

    复制代码
    [root@node10 python]# python3 test.py
    John拿到枪
    Tom抢到子弹
    #阻塞,是因为John和Tom同时上了枪锁和子弹锁,但是都没有解锁,造成死锁
    [root@node10 python]# python3 test.py
    John拿到枪
    John抢到子弹
    开枪玩一会
    John放下枪
    John放下子弹
    Jim拿到枪
    Tom抢到子弹
    #阻塞,这次是John顺利的玩一局,但是到Jim和Tom同时抢到枪和子弹,上锁,但是没有解锁,造成死锁
    [root@node10 python]# python3 test.py
    John拿到枪
    John抢到子弹
    开枪玩一会
    John放下枪
    John放下子弹
    Tom抢到子弹
    Jim拿到枪
    #阻塞
    复制代码

    5 递归锁介绍

    上几把锁,解几把锁

    复制代码
    from threading import Thread,RLock
    rlock = RLock()
    def func(name):
            rlock.acquire()
            print(name,1)
            rlock.acquire()
            print(name,2)
            rlock.acquire()
            print(name,3)
            rlock.release()
            rlock.release()
            rlock.release()
    
    for i in range(10):
            t = Thread(target=func,args=("name%s" % (i),) )
            t.start()
    
    print("程序执行结束")
    复制代码

    执行

    复制代码
    [root@node10 python]# python3 ceshi.py
    name0 1
    name0 2
    name0 3
    name1 1
    name1 2
    name1 3
    name2 1
    name2 2
    name2 3
    name3 1
    name3 2
    name3 3
    程序执行结束
    name4 1
    name4 2
    name4 3
    name5 1
    name5 2
    name5 3
    name6 1
    name6 2
    name6 3
    name7 1
    name7 2
    name7 3
    name8 1
    name8 2
    name8 3
    name9 1
    name9 2
    name9 3
    复制代码

    6 利用递归锁,解决死锁现象

    临时用于快速解决服务器崩溃死锁的问题,用递归锁应急问题

    复制代码
    from threading import Thread,RLock
    import time
    #首先创建递归锁
    gun=bullet = RLock()
    #定义第一种,先抢到枪,再抢到子弹
    def play1(name):
            #抢到枪,上锁
            gun.acquire()
            print("%s拿到枪"%(name))
            #抢到子弹上锁
            bullet.acquire()
            print ("%s抢到子弹"%(name))
            print ("开枪玩一会")
    
            time.sleep(0.5)
            #放下枪,解锁
            gun.release()
            print("%s放下枪"%(name))
            #放下子弹,解锁
            bullet.release()
            print("%s放下子弹"%(name))
    
    #定义第二种,先抢到子弹,再抢到枪
    def play2(name):
            #抢到子弹上锁
            bullet.acquire()
            print ("%s抢到子弹"%(name))
             #抢到枪,上锁
            gun.acquire()
            print("%s拿到枪"%(name))
            print ("开枪玩一会")
            time.sleep(0.5)
            #放下子弹,解锁
            bullet.release()
            print("%s放下子弹"%(name))
            #放下枪,解锁
            gun.release()
            print("%s放下枪"%(name))
    
    
    name_lst1 = ["John","Jim"]
    name_lst2 = ["Tom","Jerry"]
    for name  in  name_lst1:
            Thread(target=play1,args=(name,)).start()
    for name  in  name_lst2:
            Thread(target=play2,args=(name,)).start()
    复制代码

    执行

    复制代码
    [root@node10 python]# python3 test.py
    John拿到枪
    John抢到子弹
    开枪玩一会
    John放下枪
    John放下子弹
    Jim拿到枪
    Jim抢到子弹
    开枪玩一会
    Jim放下枪
    Jim放下子弹
    Tom抢到子弹
    Tom拿到枪
    开枪玩一会
    Tom放下子弹
    Tom放下枪
    Jerry抢到子弹
    Jerry拿到枪
    开枪玩一会
    Jerry放下子弹
    Jerry放下枪
    复制代码

    执行成功,神奇

    7 使用互斥锁解决

    从语法上来看,锁是可以互相嵌套的,但是不要使用

    上一次锁,就对应解开一把锁,形成互斥锁

    拿枪和拿子弹是同时的,上一把锁就够了,不要分开上锁,也不要去写锁的嵌套,容易死锁

    复制代码
    from threading import Thread,Lock
    import time
    #首先创建递归锁
    mylock = Lock()
    #定义第一种,先抢到枪,再抢到子弹
    def play1(name):
            #抢到枪和子弹,上锁
            mylock.acquire()
            print("%s拿到枪"%(name))
            print ("%s抢到子弹"%(name))
    
            print ("开枪玩一会")
            time.sleep(0.5)
            #放下枪,和子弹解锁
            print("%s放下枪"%(name))
            print("%s放下子弹"%(name))
            mylock.release()
    
    #定义第二种,先抢到子弹,再抢到枪
    def play2(name):
            #抢到枪子弹上锁
            mylock.acquire()
            print ("%s抢到子弹"%(name))
            print("%s拿到枪"%(name))
    
            print ("%s开枪玩一会"%(name))
            time.sleep(0.5)
            #放枪下子弹,解锁
            print("%s放下子弹"%(name))
            print("%s放下枪"%(name))
            mylock.release()
    
    
    name_lst1 = ["John","Jim"]
    name_lst2 = ["Tom","Jerry"]
    for name  in  name_lst1:
            Thread(target=play1,args=(name,)).start()
    for name  in  name_lst2:
            Thread(target=play2,args=(name,)).start()
    复制代码

    执行

    复制代码
    [root@node10 python]# python3 test.py
    John拿到枪
    John抢到子弹
    开枪玩一会
    John放下枪
    John放下子弹
    Jim拿到枪
    Jim抢到子弹
    开枪玩一会
    Jim放下枪
    Jim放下子弹
    Tom抢到子弹
    Tom拿到枪
    Tom开枪玩一会
    Tom放下子弹
    Tom放下枪
    Jerry抢到子弹
    Jerry拿到枪
    Jerry开枪玩一会
    Jerry放下子弹
    Jerry放下枪
    复制代码

    完成

    学习记录,小白一枚
  • 相关阅读:
    solr
    2.配置Flutter代码编辑器(IDE)
    1.Flutter的下载安装和环境配置
    ReactNative开发环境配置,新手踩坑必备.我也是新手
    汉字转拼音,获取汉字首字母
    For循环性能优化
    JavaScript滑块简易取色器
    C# 获取汉字拼音首字母(修正X问题,真正修正)
    团队项目-个人博客5.31
    团队项目-个人博客5.30
  • 原文地址:https://www.cnblogs.com/wangsirde0428/p/14322800.html
Copyright © 2011-2022 走看看