# 任何语言都会发生多线程,会出现不同步的问题,同步锁、死锁、递归锁
# 异步: 多任务, 多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多条运行主线
# 同步: 多任务, 多个任务之间执行的时候要求有先后顺序,必须一个先执行完成之后,另一个才能继续执行, 只有一个主线
# 阻塞:从调用者的角度出发,如果在调用的时候,被卡住,不能再继续向下运行,需要等待,就说是阻塞
# 非阻塞: 从调用者的角度出发, 如果在调用的时候,没有被卡住,能够继续向下运行,无需等待,就说是非阻塞
# def add():
# num = 1
# for i in range(100000000):
# num += i
# print(num)
# def mul():
# sum2 = 1
# for i in range(1,100000):
# sum2 *=i
# print(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()
# print('Cost time %s'%(time.time()-start))
####################################################################################################################
# GIL:全局解释锁,python内置解释器中定义的,在同一个时刻,只能有一个线程被cpu执行
# 任务类型分为:1.IO密集型;2.计算密集型
# 对于IO密集型(使用多数):python可以使用多线程效率高,也可以采用多线程+协程
# 对于计算密集型:python的多线程就没有用了,由于cpython内置解释器的设计问题,充分保证了进程安全,不能同时处理计算进程
####################################################################################################################
#——————————————————————同步锁————————————————————————————————#
# import threading,time
# def sub():
# global num
# #——————————————————————————————#
# lock.acquire()#锁定,锁定后变成了串行
# temp = num
# time.sleep(0.02)
# num = temp-1
# lock.release()#解锁,释放资源
# # ——————————————————————————————#
# num = 100
# start = time.time()
# l = []
# lock = threading.Lock()
# for i in range(100):
# t = threading.Thread(target=sub)
# t.start()
# l.append(t)
# for i in l:
# t.join()
# print(num,time.time()-start)
#——————————————————————同步锁————————————————————————————————#
#————————————————————线程死锁和递归锁—————————————————————————————#
# 在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,
# 就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去
# RLock 递归锁,内部维护了一个计数器,acquire和release需要共同出现
# 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,
# 因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
# 此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁
# 解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
# 这个RLock内部维护着一个Lock和一个counter变量,
# counter记录了acquire的次数,从而使得资源可以被多次require。
# 直到一个线程所有的acquire都被release,其他的线程才能获得资源。
# 上面的例子如果使用RLock代替Lock,则不会发生死锁:
# import threading,time
# class MyThread(threading.Thread):
# def actionA(self):
# r_lock.acquire()
# print(self.name,'getA',time.ctime())
# time.sleep(1)
# r_lock.acquire()
# print(self.name, 'getB', time.ctime())
# time.sleep(1)
# r_lock.release()
# r_lock.release()
# def actionB(self):
# r_lock.acquire()
# print(self.name, 'getB', time.ctime())
# time.sleep(1)
# r_lock.acquire()
# print(self.name, 'getA', time.ctime())
# time.sleep(1)
# r_lock.release()
# r_lock.release()
# def run(self):
# self.actionA()
# time.sleep(0.5)
# self.actionB()
#
#
#
# if __name__ == '__main__':
# r_lock = threading.RLock()
# A = threading.Lock()
# B = threading.Lock()
# L = []
# for i in range(5):
# t = MyThread()
# t.start()
# L.append(t)
# for i in L:
# i.join()
# print('ending....')
####################################################################################################################
# 同步条件(Event)
# 同进程的一样
# 线程的一个关键特性是每个线程都是独立运行且状态不可预测。
# 如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,
# 这时线程同步问题就会变得非常棘手。
# 为了解决这些问题,我们需要使用threading库中的Event对象。
# 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。
# 在初始情况下,Event对象中的信号标志被设置为假。
# 如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。
# 一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。
# 如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行
# event.isSet():返回event的状态值;
# event.wait():如果 event.isSet()==False将阻塞线程;
# event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
# event.clear():恢复event的状态值为False。
####################################################################################################################
# 信号量:同时允许开启的线程个数
# 同进程的一样
# Semaphore管理一个内置的计数器,
# 每当调用acquire()时内置计数器-1;
# 调用release() 时内置计数器+1;
# 计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
# 实例:(同时只有5个线程可以获得semaphore,即可以限制最大连接数为5):
####################################################################################################################
# 队列:队列是一种数据结构,列表是不安全的数据结构,多线程利器
# 队列的三种模式,先进先出,先进后出,按优先级进行
# 默认模式是先进先出
# import queue,time #线程队列
# q = queue.Queue()
# q.put(12)
# q.put('asd')
# q.put(14)
# q.put({'name':'panda'})
# while 1:
# data = q.get()
# print(data)
# time.sleep(1)
# print('-----')
# ####################################################################################################################
# 生产者消费者模型
# 为什么要使用生产者和消费者模式
# 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。
# 在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,
# 那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,
# 如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
#
# 什么是生产者消费者模式
#
# 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。
# 生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,
# 所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,
# 消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
# 这就像,在餐厅,厨师做好菜,不需要直接和客户交流,而是交给前台,
# 而客户去饭菜也不需要不找厨师,直接去前台领取即可,这也是一个结耦的过程。
# import time,random
# import queue,threading
# q = queue.Queue()
# def Producer(name):
# count = 0
# while count <10:
# print('正在制作')
# time.sleep(random.randrange(3))
# q.put(count)
# print('%s厨师%s号做好了一盘菜'%(name,count))
# count +=1
# print('做完了')
# def Consumer(name):
# count = 0
# while count <10:
# time.sleep(random.randrange(4))
# if not q.empty():
# data = q.get()
# print(data)
# print(' 33[32;1m大胃王 %s 吃掉了 %s 一盘菜... 33[0m' % (name, data))
# else:
# print("-----吃的太快啦----")
# count += 1
# p1 = threading.Thread(target=Producer, args=('A',))
# c1 = threading.Thread(target=Consumer, args=('B',))
# p1.start()
# c1.start()