Python 线程同步,Queue,死锁及其解决(Timer,信号量semphore,timeout,RLock等)------26

 

共享变量/线程同步

  • 多个线程同时访问同一个变量时会产生共享变量问题
  • 使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。
  • 解决办法:锁,信号灯
    • 锁(Lock)
      • 是一个标志,表示一个线程在占用一些资源
      • 使用方法
        • 对资源上锁
        • 使用资源
        • 取消锁,释放锁

线程安全问题

  • 如果一个资源/变量,对于多线程来讲,不用加锁也会引起任何问题,则称为线程安全
  • 线程不安全的变量类型:list,set,dict
  • 线程安全的变量类型:queue

In [31]:

 


 
#本段代码中由于sum为myAdd与myMinu的共享变量,而且并未合理设置,出现的结果未达到预期结果
import threading
sum = 0 
loopSum = 100000
def myAdd():
    global sum,loopSum
    for i in range(1,loopSum+1):
        sum += 1

 
def myMinu():
    global sum,loopSum
    for i in range(1,loopSum+1):
        sum -= 1

 
if __name__ == '__main__':
    print('开始了')
    t1 = threading.Thread(target=myAdd,args=())
    t2 = threading.Thread(target=myMinu,args=())

 
    t1.start()
    t2.start()

 
    t1.join()
    t2.join()

 
    print('结果是:{0}'.format(sum))
开始了
结果是:-23806

In [47]:

 

#锁  实例
import threading
sum = 0 
loopSum = 100000
lock = threading.Lock()
def myAdd():
    global sum,loopSum
    for i in range(1,loopSum+1):
        #上锁
        lock.acquire()
        sum += 1
        #释放锁
        lock.release()
def myMinu():
    global sum,loopSum
    for i in range(1,loopSum+1):
        #上锁
        lock.acquire()
        sum -= 1
        #释放锁
        lock.release()

 
if __name__ == '__main__':
    print('开始了是{0}'.format(sum))
    t1 = threading.Thread(target=myAdd,args=())
    t2 = threading.Thread(target=myMinu,args=())

 
    t1.start()
    t2.start()

 
    t1.join()
    t2.join()
    print('结果是:{0}'.format(sum))
开始了是0
结果是:0

线程优先级队列( Queue)

  • Python的Queue模块中提供了同步的、线程安全的队列类,包括
    • FIFO(先入先出)队列Queue
    • LIFO(后入先出)队列LifoQueue
    • 优先级队列PriorityQueue
  • 这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。
  • Queue模块中的常用方法:
    • Queue.qsize() 返回队列的大小
    • Queue.empty() 如果队列为空,返回True,反之False
    • Queue.full() 如果队列满了,返回True,反之False
    • Queue.full 与 maxsize 大小对应
    • Queue.get([block[, timeout]])获取队列,timeout等待时间
    • Queue.get_nowait() 相当Queue.get(False)
    • Queue.put(item) 写入队列,timeout等待时间
    • Queue.put_nowait(item) 相当Queue.put(item, False)
    • Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
    • Queue.join() 实际上意味着等到队列为空,再执行别的操作

In [61]:

 

import queue
import time
import threading
class Producer(threading.Thread):
    def run(self):
        global qu
        count = 0
        while True:
            if qu.qsize()<100:
                for i in range(100):
                    count = count + 1
                    msg = '生产产品'+str(count)
                    qu.put(msg)
                    print(msg)
            time.sleep(0.5)
class Consumer(threading.Thread):
    def run(self):
        global qu
        while True:#无线循环
            if qu.qsize()>10:
                for i in range(3):
                    msg = self.name + '消费了' +qu.get()
                    print(msg)
            time.sleep(1)

 
if __name__ == '__main__':
    qu = queue.Queue()
    for i in range(500):
        qu.put('初始化产品'+str(i))
    for i in range(2):
        p = Producer()
        p.start()
    for i in range(5):

 
        c = Consumer()
        c.start()
Thread-145消费了初始化产品0
Thread-145消费了初始化产品1
Thread-145消费了初始化产品2
Thread-146消费了初始化产品3
Thread-146消费了初始化产品4
Thread-146消费了初始化产品5
Thread-147消费了初始化产品6
Thread-147消费了初始化产品7
Thread-147消费了初始化产品8
Thread-148消费了初始化产品9
Thread-148消费了初始化产品10
Thread-148消费了初始化产品11
Thread-149消费了初始化产品12
Thread-149消费了初始化产品13
Thread-149消费了初始化产品14
Thread-118消费了初始化产品15Thread-115消费了初始化产品18Thread-114消费了初始化产品21Thread-116消费了初始化产品24Thread-117消费了初始化产品27


#进入死循环



死锁/解决死锁

  • 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
  • 添加timeout 锁的等待时间
  • Timer: 隔一定时间调用一个函数,如果想实现每隔一段时间就调用一个函数的话,就要在Timer调用的函数中,再次设置Timer。Timer是Thread的一个派生类
  • semphore (信号量) 允许一个资源最多有几个线程同时使用
    • threading模块里的Semaphore类实现了信号量对象,可用于控制获取资源的线程数量。
    • 所具有的acquire()和release()方法,能够使用with语句的上下文管理器。
    • 当进入时,将调用acquire()方法,当退出时,将调用release()。
    • acquire(blocking=True, timeout=None):
    • timeout设置超时秒。如果未在时间间隔内完成,返回false,否则返回true
    • 没有blocking返回false,否则返回true。
    • release():释放一个信号量

递归锁

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

In [3]:

 

#死锁
from threading import Thread,Lock
import time
mutexA=Lock()
mutexB=Lock()
class MyThread(Thread):
    def run(self):
        self.func1()
        self.func2()
    def func1(self):
        mutexA.acquire()
        print('%s 拿到A锁' %self.name)
        mutexB.acquire()
        print('%s 拿到B锁' %self.name)
        mutexB.release()
        mutexA.release()
    def func2(self):
        mutexB.acquire()
        print('%s 拿到B锁' %self.name)
        time.sleep(2)
        mutexA.acquire()
        print('%s 拿到A锁' %self.name)
        mutexA.release()
        mutexB.release()
if __name__ == '__main__':
    for i in range(10):
        t=MyThread()
        t.start()
Thread-24 拿到A锁
Thread-24 拿到B锁
Thread-24 拿到B锁
Thread-25 拿到A锁

In [4]:

 

#信号量
import threading
import time
s = threading.Semaphore(2) 
def func():
    if s.acquire():
        for i in range(3):
            print('调用线程 {0}'.format(i))
            time.sleep(10)
            print('释放线程 {0}'.format(i))
            s.release()
for i in range(4):
    t = threading.Thread(target=func)
    t.start()
调用线程 0
调用线程 0
释放线程 0
调用线程 1
释放线程 0
调用线程 1
调用线程 0
调用线程 0
释放线程 1释放线程 1释放线程 0


调用线程 2调用线程 1调用线程 2


释放线程 0
调用线程 1
释放线程 2释放线程 2释放线程 1释放线程 1



调用线程 2调用线程 2

释放线程 2释放线程 2

In [*]:

 

#Timer
import threading
import time
def func():
    print('running,running...')
    time.sleep(2)
    print('done  done')
if __name__ == "__main__":
    t = threading.Timer(3,func)
    t.start()
    i = 0 
    while i<6:
        print('{0}hahahahaha'.format(i))
        time.sleep(5)
        i += 1

In [1]:

 

#RLock()
import threading
import time
class MyThread(threading.Thread):
      def run(self):
            global num
            time.sleep(1)
            if mutex.acquire(1):
                num = num+1
                msg = self.name+' set num to '+str(num)
                print (msg)
                mutex.acquire()
                mutex.release()
                mutex.release()
num = 0
mutex = threading.RLock()
def test1():
    for i in range(5):
        t = MyThread()
        t.start()
if __name__ == '__main__':
    test1()
Thread-4 set num to 1Thread-5 set num to 2

Thread-6 set num to 3
Thread-7 set num to 4
Thread-8 set num to 5
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

What’smean

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

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

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

打赏作者

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

抵扣说明:

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

余额充值