线程锁是怎么回事?
内容衔接:
咱们要明白一件事就是我们现在在这里讲的锁跟前面所说的GIL锁不是一个锁,前面说的GIL锁
是Python内置的一个全局解释器,这个锁的目的就是保证在同一时刻进程中只有一个线程可以被CPU
调度.
但是我们也说了这个GIL锁有的时候并不能更快解决我们把语言翻译出来的目的,那比如说在密集
型计算问题的时候,反而会比较慢,那有的人就说了,那为什么不把这锁给去了?
这个问题我其实也一直在纠结,存在即合理,那么站在可观的角度去看这个问题,应该能给你解答
那么在Python开始被程序员使用的时候也有的就提出了这样的文问题,那么Python 的创始人的团队
也就在处理这个问题,但是当他们按照Java和C#的解决方案去解决这个问题的时候就发现,没有GIL锁
反而比有GIL锁跑的更慢了.所以有改回来了,但是一方面的原因还是因为他们团队代码写的不够好(个人
主见仅供参考)这一直以来成为了Python的一大诟病,但是我相信在Pyhton如此之火的现在而言解决
这个问题就是迟早的事情
扯了这么多,连主题都忘了继续说到我们今天要说的锁的事那么我们今天要说的锁2到底是什么呢>
给你就一个例子,就是说这样一个 场景,比如说我现在有有两个函数;里面都要去调用公共变量的一个
值但是我们运行的时候由于这个GIL锁的原因,我第一个程序还没有将公共变量中值改好但是我的使用
次数已经够了,就先不能继续调度CPU换下一个程序了,那么我在下一个代码中要用上一个改好的数据
但是前面的就没有改好,这样就会造成后面的都错了.
那么我们能不能用一个锁去把我当前要执行的程序先锁起来,当我执行完了,你们在去调度CPU
这样最起码不会造成后面的程序出错,那么这个锁就是我们今天要说的
1.锁 Lock(一次放一个)
线程安全:多线程操作时,内部会让所有线程排队处理.例如:list\dict\Queue(简单的说就是还没有干完就
不让用CPU了)
那么还有一些是线程不安全的怎么办呢?
线程不安全+锁=排队处理 这样就安全了
Lock用法
import threading import time v = [] lock = threading.Lock() def func(arg): lock.acquire() v.append(arg) time.sleep(0.01) m = v[-1] print(arg,m) lock.release() for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
引入时间模块是为了让结果显示的更加贴切,在实际使用的时候是不需要加时间模块的,
这里需要注意两点在应用是func()函数中一定要有lock.acquire 相当于上锁
lock.release()相当于开锁必须要的还有注意的就是只能锁一次,多次锁就解不开了
2.锁 Rlock(一次放一个)
Rlock实例
v = [] lock = threading.RLock() def func(arg): lock.acquire() lock.acquire() v.append(arg) time.sleep(0.01) m = v[-1] print(arg,m) lock.release() lock.release() for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() 用法相似于lock但是可以多次锁,这个锁比较常用
3.锁 BoundedSemaphore(一次放N个)信号量
实例:
import time import threading lock = threading.BoundedSemaphore(3) def func(arg): lock.acquire() print(arg) time.sleep(1) lock.release() for i in range(20): t =threading.Thread(target=func,args=(i,)) t.start()
4.锁 Condition(一次放X个)
实例:
import time
import threading
lock = threading.Condition()
# ############## 方式一 ##############
def func(arg):
print('线程进来了')
lock.acquire()
lock.wait() # 加锁
print(arg)
time.sleep(1)
lock.release()
for i in range(10):
t =threading.Thread(target=func,args=(i,))
t.start()
while True:
inp = int(input('>>>'))
lock.acquire()
lock.notify(inp)
lock.release()
# ############## 方式二 ##############
"""
def xxxx():
print('来执行函数了')
input(">>>")
# ct = threading.current_thread() # 获取当前线程
# ct.getName()
return True
def func(arg):
print('线程进来了')
lock.wait_for(xxxx)
print(arg)
time.sleep(1)
for i in range(10):
t =threading.Thread(target=func,args=(i,))
t.start()
"""
5.锁 Event(一次放所有)
实例:
import time import threading lock = threading.Event() def func(arg): print('线程来了') lock.wait() # 加锁:红灯 print(arg) for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() input(">>>>") lock.set() # 绿灯 lock.clear() # 再次变红灯 for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() input(">>>>") lock.set()
总结:
线程安全,列表和字典线程安全,只是在Python中在Java中不一定安全
为什么要加锁?
1.因为有的线程不安全,让它变的安区
2.想控制一段代码
6.threading.local
作用:
内部自动为每一个线程维护一个空间(字典).用于存放自己的值,保证线程之间的数据隔离.
实例:
import time import threading v = threading.local() def func(arg): # 内部会为当前线程创建一个空间用于存储:phone=自己的值 v.phone = arg time.sleep(2) print(v.phone,arg) # 去当前线程自己空间取值 for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
threadinglocal原理
ATA_DICT = {} def func(arg): ident = threading.get_ident() DATA_DICT[ident] = arg time.sleep(1) print(DATA_DICT[ident],arg) for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
import time import threading INFO = {} class Local(object): def __getattr__(self, item): ident = threading.get_ident() return INFO[ident][item] def __setattr__(self, key, value): ident = threading.get_ident() if ident in INFO: INFO[ident][key] = value else: INFO[ident] = {key:value} obj = Local() def func(arg): obj.phone = arg # 调用对象的 __setattr__方法(“phone”,1) time.sleep(2) print(obj.phone,arg) for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
7.线程池from concurrent.futures import ThreadPoolExecutor
import time def task(a1,a2): time.sleep(2) print(a1,a2) # 创建了一个线程池(最多5个线程) pool = ThreadPoolExecutor(5) for i in range(40): # 去线程池中申请一个线程,让线程执行task函数。 pool.submit(task,i,8)
经常配合Rlock用
8.生产者消费者模型
三部件:
生产者:
队列:先进先出
扩展: 栈:先进后出
消费者:
生产者消费者模型解决什么问题? 答:一直不用等待的问题
实例:
import time import queue import threading q = queue.Queue() # 线程安全 def producer(id): """ 生产者 :return: """ while True: time.sleep(2) q.put('包子') print('厨师%s 生产了一个包子' %id ) for i in range(1,4): t = threading.Thread(target=producer,args=(i,)) t.start() def consumer(id): """ 消费者 :return: """ while True: time.sleep(1) v1 = q.get() print('顾客 %s 吃了一个包子' % id) for i in range(1,3): t = threading.Thread(target=consumer,args=(i,)) t.start()