Python中的多线程同步(线程锁,队列)

线程间的资源竞争

#!/user/bin/env python
#-*-coding utf-8-*-
#@Time           : 2020/6/158:44
#@Author         : GodSpeed
#@File           : 线程的资源竞争.py
#@Software       : PyCharm

import threading
import time
import dis #查看Python代码在cpu的运行轨迹
##print(dis.dis(fun))


g_num = 0

def demo1(args):
    global g_num


    print(args+'---------开始运行-------------'+str(g_num))
    for i in range(50*100000):
        g_num += 1
        #print("I am demo1---{} g_num={}次运行\n".format(i,g_num))

    print(args + '-----------运行结束-----------'+str(g_num))


def demo2(args):
    global g_num

    print(args+'---------开始运行-------------'+str(g_num))
    for i in range(30*100000):
        g_num += 1
        #print("I am demo2---{} g_num={}次运行\n".format(i,g_num))

    print(args + '-----------运行结束-----------'+str(g_num))
    print('\n')

def main():
    t1 = threading.Thread(target=demo1,args=("demo1",))
    t2 = threading.Thread(target=demo2,args=("demo2",))
    t1.start()
    t2.start()



if __name__ == '__main__':
    main()


结果:
demo1---------开始运行-------------0
demo2---------开始运行-------------97535
demo2-----------运行结束-----------3431958
demo1-----------运行结束-----------5370013

问题:
线程1和线程2出现了资源强制,导致num并没有有序的增加,缺失了很多数据

互斥锁和死锁

某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程 不能改变,只到该线程释放资源,将资源的状态变成"⾮锁定",其他的线程才能再次锁定该资源。互斥锁保证了每次只有⼀个线程进⾏写⼊操作,从⽽保证了多线程情况下数据的正确性。

#!/user/bin/env python
#-*-coding utf-8-*-
#@Time           : 2020/6/158:44
#@Author         : GodSpeed
#@File           : 线程的资源竞争.py
#@Software       : PyCharm

import threading
import time
import dis #查看Python代码在cpu的运行轨迹
##print(dis.dis(fun))

#创建锁
mutex = threading.Lock()

g_num = 0

def demo1(args):
    global g_num

    #上锁
    mutex.acquire()

    print(args+'---------开始运行-------------'+str(g_num))
    for i in range(50*100000):
        g_num += 1
        #print("I am demo1---{} g_num={}次运行\n".format(i,g_num))
    # 解锁
    mutex.release()
    print(args + '-----------运行结束-----------'+str(g_num))
    

def demo2(args):
    global g_num
    # 上锁
    mutex.acquire()

    print(args+'---------开始运行-------------'+str(g_num))
    for i in range(30*100000):
        g_num += 1
        #print("I am demo2---{} g_num={}次运行\n".format(i,g_num))
    # 解锁
    mutex.release()
    print(args + '-----------运行结束-----------'+str(g_num))


def main():
    t1 = threading.Thread(target=demo1,args=("demo1",))
    t2 = threading.Thread(target=demo2,args=("demo2",))
    t1.start()
    t2.start()

if __name__ == '__main__':
    main()


结果:
demo1---------开始运行-------------0
demo1-----------运行结束-----------5000000
demo2---------开始运行-------------5000000
demo2-----------运行结束-----------8000000

多重锁

#!/user/bin/env python
#-*-coding utf-8-*-
#@Time           : 2020/6/158:44
#@Author         : GodSpeed
#@File           : 线程的资源竞争.py
#@Software       : PyCharm

import threading
import time
import dis #查看Python代码在cpu的运行轨迹
##print(dis.dis(fun))

#创建锁
mutex = threading.Lock()
mutex1 = threading.Lock()
g_num = 0

def demo1(args):
    global g_num

    #上锁
    mutex.acquire()
    #mutex1.acquire()
    mutex.acquire()

    print(args+'---------开始运行-------------'+str(g_num))
    for i in range(50*100000):
        g_num += 1
        #print("I am demo1---{} g_num={}次运行\n".format(i,g_num))

    #mutex1.release()
    # 解锁 
    mutex.release()
    mutex.release()
    print(args + '-----------运行结束-----------'+str(g_num))


def demo2(args):
    global g_num
    # 上锁
    mutex.acquire()

    print(args+'---------开始运行-------------'+str(g_num))
    for i in range(30*100000):
        g_num += 1
        #print("I am demo2---{} g_num={}次运行\n".format(i,g_num))
    # 解锁
    mutex.release()
    print(args + '-----------运行结束-----------'+str(g_num))


def main():
    t1 = threading.Thread(target=demo1,args=("demo1",))
    t2 = threading.Thread(target=demo2,args=("demo2",))
    t1.start()
    t2.start()



if __name__ == '__main__':
    main()

结果
同一锁名,多重上锁,threading.Lock无法释放

解决方法,用RLock

#!/user/bin/env python
#-*-coding utf-8-*-
#@Time           : 2020/6/158:44
#@Author         : GodSpeed
#@File           : 线程的资源竞争.py
#@Software       : PyCharm

import threading
import time
import dis #查看Python代码在cpu的运行轨迹
##print(dis.dis(fun))

#创建锁
#mutex = threading.Lock()
mutex = threading.RLock()
mutex1 = threading.Lock()
g_num = 0

def demo1(args):
    global g_num

    #上锁
    mutex.acquire()
    #mutex1.acquire()
    mutex.acquire()

    print(args+'---------开始运行-------------'+str(g_num))
    for i in range(50*100000):
        g_num += 1
        #print("I am demo1---{} g_num={}次运行\n".format(i,g_num))

    #mutex1.release()
    # 解锁
    mutex.release()
    mutex.release()
    print(args + '-----------运行结束-----------'+str(g_num))


def demo2(args):
    global g_num
    # 上锁
    mutex.acquire()

    print(args+'---------开始运行-------------'+str(g_num))
    for i in range(30*100000):
        g_num += 1
        #print("I am demo2---{} g_num={}次运行\n".format(i,g_num))
    # 解锁
    mutex.release()
    print(args + '-----------运行结束-----------'+str(g_num))


def main():
    t1 = threading.Thread(target=demo1,args=("demo1",))
    t2 = threading.Thread(target=demo2,args=("demo2",))
    t1.start()
    t2.start()



if __name__ == '__main__':
    main()

结果
demo1---------开始运行-------------0
demo1-----------运行结束-----------5000000
demo2---------开始运行-------------5000000
demo2-----------运行结束-----------8000000

死锁

在线程间共享多个资源的时候,如果两个线程分别占有⼀部分资源并且同时等
待对⽅的资源,就会造成死锁。

#!/user/bin/env python
#-*-coding utf-8-*-
#@Time           : 2020/6/1513:24
#@Author         : GodSpeed
#@File           : 死锁.py
#@Software       : PyCharm



import threading
import time

class MyThread1(threading.Thread):
    def run(self):
        # 对mutexA上锁
        mutexA.acquire()

        # mutexA上锁后,延时1秒,等待另外那个线程 把mutexB上锁
        print(self.name+'----do1---up----')
        time.sleep(1)

        # 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了
        mutexB.acquire()
        print(self.name+'----do1---down----')
        mutexB.release()

        # 对mutexA解锁
        mutexA.release()

class MyThread2(threading.Thread):
    def run(self):
        # 对mutexB上锁
        mutexB.acquire()

        # mutexB上锁后,延时1秒,等待另外那个线程 把mutexA上锁
        print(self.name+'----do2---up----')
        time.sleep(1)

        # 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了
        mutexA.acquire()
        print(self.name+'----do2---down----')
        mutexA.release()

        # 对mutexB解锁
        mutexB.release()

mutexA = threading.Lock()
mutexB = threading.Lock()

if __name__ == '__main__':
    t1 = MyThread1()
    t2 = MyThread2()
    t1.start()
    t2.start()



结果
Thread-1----do1—up----
Thread-2----do2—up----

被锁住,无法解锁

Queue线程

在线程中,访问⼀些全局变量,加锁是⼀个经常的过程。如果你是想把⼀些数
据存储到某个队列中,那么Python内置了⼀个线程安全的模块叫做queue模 块。Python中的queue模块中提供了同步的、线程安全的队列类,包括 FIFO(先进先出)队列Queue,LIFO(后⼊先出)队列LifoQueue。这些队列
都实现了锁原语(可以理解为原⼦操作,即要么不做,要么都做完),能够在
多线程中直接使⽤。可以使⽤队列来实现线程间的同步。

#Queue 对数据结构中的栈和队列的封装 先进后出

from queue import Queue


qu = Queue(3) #创建⼀个先进先出的队列。
#qsize() # 返回队列的⼤⼩。
print(qu.qsize())

#empty() # 判断队列是否为空。
if qu.empty():
    print('队列为空')


#put()   # 将⼀个数据放到队列中。

qu.put([1,2])
qu.put([3,4])
qu.put([5,6])

print(qu.full())

if qu.full():
    print('队列已满,无法添加')
else:
    qu.put(['a','b','c','d','e','f']) #如果不加队列是否满的加判断超过队列的size会出现堵塞

#也可以设置超时时间
#qu.put(1,timeout=2) #报错queue.Full
#qu.put_nowait(3) #报错queue.Full
print('---------------------------')
#get()   # 从队列中取最后⼀个数据。
print(qu.get())
qu.put_nowait(3)
print(qu.get())
print(qu.get())
print(qu.get())
#print(qu.get()) 获取次数,超过队列大小,会阻塞
#print(qu.get_nowait()) #超过范围直接报错 _queue.Empty

线程同步实例

方式一:

# !/user/bin/env python
# -*-coding utf-8-*-
# @Time           : 2020/6/1515:42
# @Author         : GodSpeed
# @File           : condition.py
# @Software       : PyCharm

import threading

from threading import Condition

class Question(threading.Thread):
    def __init__(self, condition):
        super().__init__(name='提问')
        self.condition = condition

    def run(self) -> None:
        self.condition.acquire()
        print('A1问:什么叫见过大世面?')
        self.condition.notify()

        self.condition.wait()
        print('A2问:怎样控制住自己不发脾气?')
        self.condition.notify()

        self.condition.wait()
        print('A3问:太在乎自己在别人眼中的形象,导致活得很累,怎么办?')
        self.condition.notify()

        self.condition.wait()
        print('A4问:刚刚看到有人说中国有50个城市的白领平均月薪达到了8730元?')
        self.condition.notify()

        self.condition.wait()

        print('A5问:吃什么最补脑?')
        self.condition.notify()

        self.condition.wait()

        print('A6问:你怎么一个人出来逛街?')
        self.condition.notify()

        self.condition.release()


class Answer(threading.Thread):

    def __init__(self, condition):
        super().__init__(name='回答')
        self.condition = condition

    def run(self) -> None:
        self.condition.acquire()

        #self.condition.wait()

        print('B1神回复:能享受最好的,能承受最坏的。')
        self.condition.notify()

        self.condition.wait()
        print('B2神回复:如果你是对的,你没必要发脾气;如果你是错的,你没资格发脾气。')
        self.condition.notify()

        self.condition.wait()
        print('B3神回复:没有那么多人关注你,所以,放轻松点,你没有那么多观众。')
        self.condition.notify()

        self.condition.wait()
        print('B4神回复 :平均工资能说明什么问题?潘长江和姚明平均身高是196CM,能说明什么?潘长江很高吗?')
        self.condition.notify()

        self.condition.wait()
        print('B5神回复:吃亏。')
        self.condition.notify()

        self.condition.wait()
        print('B6神回复:半个人我怕吓着你啊!')
        condition.notify()
        self.condition.release()

if __name__ == '__main__':
    #lock = threading.RLock()
    condition = Condition()

    qu = Question(condition)
    an = Answer(condition)

    qu.start()
    an.start()


结果:
A1问:什么叫见过大世面?
B1神回复:能享受最好的,能承受最坏的。
A2问:怎样控制住自己不发脾气?
B2神回复:如果你是对的,你没必要发脾气;如果你是错的,你没资格发脾气。
A3问:太在乎自己在别人眼中的形象,导致活得很累,怎么办?
B3神回复:没有那么多人关注你,所以,放轻松点,你没有那么多观众。
A4问:刚刚看到有人说中国有50个城市的白领平均月薪达到了8730元?
B4神回复 :平均工资能说明什么问题?潘长江和姚明平均身高是196CM,能说明什么?潘长江很高吗?
A5问:吃什么最补脑?
B5神回复:吃亏。
A6问:你怎么一个人出来逛街?
B6神回复:半个人我怕吓着你啊!

注意:调用必须逆序,否则会造成阻塞

方式二:


#!/user/bin/env python
#-*-coding utf-8-*-
#@Time           : 2020/6/1516:52
#@Author         : GodSpeed
#@File           : 线程同步方式二.py
#@Software       : PyCharm


# !/user/bin/env python
# -*-coding utf-8-*-
# @Time           : 2020/6/1515:42
# @Author         : GodSpeed
# @File           : condition.py
# @Software       : PyCharm

import threading

from threading import Condition

class Question(threading.Thread):
    def __init__(self, condition):
        super().__init__(name='提问')
        self.condition = condition

    def run(self) -> None:
        #self.condition.acquire()

        with self.condition:
            print('A1问:什么叫见过大世面?')
            self.condition.notify()

            self.condition.wait()
            print('A2问:怎样控制住自己不发脾气?')
            self.condition.notify()

            self.condition.wait()
            print('A3问:太在乎自己在别人眼中的形象,导致活得很累,怎么办?')
            self.condition.notify()

            self.condition.wait()
            print('A4问:刚刚看到有人说中国有50个城市的白领平均月薪达到了8730元?')
            self.condition.notify()

            self.condition.wait()

            print('A5问:吃什么最补脑?')
            self.condition.notify()

            self.condition.wait()

            print('A6问:你怎么一个人出来逛街?')
            self.condition.notify()

        #self.condition.release()


class Answer(threading.Thread):

    def __init__(self, condition):
        super().__init__(name='回答')
        self.condition = condition

    def run(self) -> None:
        #self.condition.acquire()

        with self.condition:
            self.condition.wait()

            print('B1神回复:能享受最好的,能承受最坏的。')
            self.condition.notify()

            self.condition.wait()
            print('B2神回复:如果你是对的,你没必要发脾气;如果你是错的,你没资格发脾气。')
            self.condition.notify()

            self.condition.wait()
            print('B3神回复:没有那么多人关注你,所以,放轻松点,你没有那么多观众。')
            self.condition.notify()

            self.condition.wait()
            print('B4神回复 :平均工资能说明什么问题?潘长江和姚明平均身高是196CM,能说明什么?潘长江很高吗?')
            self.condition.notify()

            self.condition.wait()
            print('B5神回复:吃亏。')
            self.condition.notify()

            self.condition.wait()
            print('B6神回复:半个人我怕吓着你啊!')
            condition.notify()
        #self.condition.release()

if __name__ == '__main__':
    #lock = threading.RLock()
    condition = Condition()

    qu = Question(condition)
    an = Answer(condition)

    an.start()
    qu.start()

因为Condition类中有

    def __enter__(self):
        return self._lock.__enter__()

    def __exit__(self, *args):
        return self._lock.__exit__(*args)

所以可以采用with来代替self.condition.acquire()和self.condition.release()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Narutolxy

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值