Python中的线程锁
前言 本章节继续探讨 官方中文文档 线程安全
在聊线程安全之前,我还是决定拿之前的那个例子来描述它。
这里关于线程安全的代码测试我决定不用上面例举的这个例子,而是用多线程做密集型计算做演示,数值越大效果越明显: import threading num = 0 def add(): global num for i in range(10000000): # 一千万次 num += 1 sub(): 一千万次 num -= 1 if __name__ == '__main__': t1 = threading.Thread(target=add,) t2 = threading.Thread(target=sub,) t1.start() t2.start() t1.join() t2.join() print("最终结果:",num) ==== 执行结果 ==== 三次采集 """ 最终结果: -1472151 最终结果: -2814372 最终结果: -5149396 """ 一个加一千万次,一个减一千万次,按理来说最后的结果应该是0,但是为什么相差这么大?更加恐怖的是每次的结果都完全不一样呢? 其实这就是由于线程切换导致出的线程安全问题,因为我们不能精确的知道CPU会在什么时候进行线程的切换,那么如何控制它呢?我们就需要用到锁,其实通俗点来讲你可以这么认为他:
锁的作用锁就提供给我们能够自行操控线程切换的一种手段,而并非系统自带的切换机制进行切换。 Lock同步锁方法大全
使用方式
0 add(): lock.acquire() 上锁 一千万次 num += 1 lock.release() 解锁 sub(): lock.acquire() 一千万次 num -= 1: lock = threading.Lock() 实例化同步锁对象 t1 = threading.Thread(target= 最终结果: 0 最终结果: 0 最终结果: 0 """ 上锁 lock.acquire() 死锁 lock.release() lock.release() lock.release() lock.release() : lock = threading.Lock() 卡住不动了 """ 上下文管理 add(): with lock: 自动加锁与解锁 num 一千万次 num += 1 sub(): with lock: 一千万次 num -= 1 : lock = threading.Lock() t1 = threading.Thread(target=""" RLock递归锁方法大全
使用方式
上锁 + 1 lock.acquire() 上锁 + 1 解锁 - 1 lock.release() 解锁 - 1 : lock = threading.RLock() 实例化递归锁对象 """ Condition条件锁方法大全
使用方式
0 task(): obj = threading.current_thread() 当前是线程[{0}],已经开始运行了....format(obj.getName())) cond.acquire() .format(obj.getName())) cond.wait() 暂停,等待唤醒 num += 1 .format(obj.getName())) cond.release() 解锁 : cond = threading.Condition() 实例条件锁对象 in range(10): t1 = threading.Thread(target=task,) 开启10条线程 t1.start() 等待CPU调度执行 while num < 10: task_num = int(input(请输入你要执行的线程数量:)) cond.acquire() cond.notify(task_num) 通知唤醒 cond.release() ==== 执行结果 ==== 当前是线程[Thread-1],已经开始运行了... 当前是线程[Thread-1],处于等待状态... 当前是线程[Thread-2],已经开始运行了... 当前是线程[Thread-2],处于等待状态... 当前是线程[Thread-3],已经开始运行了... 当前是线程[Thread-3],处于等待状态... 当前是线程[Thread-4],已经开始运行了... 当前是线程[Thread-4],处于等待状态... 当前是线程[Thread-5],已经开始运行了... 当前是线程[Thread-5],处于等待状态... 当前是线程[Thread-6],已经开始运行了... 当前是线程[Thread-6],处于等待状态... 当前是线程[Thread-7],已经开始运行了... 当前是线程[Thread-7],处于等待状态... 当前是线程[Thread-8],已经开始运行了... 当前是线程[Thread-8],处于等待状态... 当前是线程[Thread-9],已经开始运行了... 当前是线程[Thread-9],处于等待状态... 当前是线程[Thread-10],已经开始运行了... 当前是线程[Thread-10],处于等待状态... 请输入你要执行的线程数量:2 当前是线程[Thread-1],等待状态结束,继续运行... 当前是线程[Thread-2],等待状态结束,继续运行... 请输入你要执行的线程数量:3 当前是线程[Thread-3],等待状态结束,继续运行... 当前是线程[Thread-5],等待状态结束,继续运行... 当前是线程[Thread-4],等待状态结束,继续运行... 请输入你要执行的线程数量:5 当前是线程[Thread-6],等待状态结束,继续运行... 当前是线程[Thread-7],等待状态结束,继续运行... 当前是线程[Thread-8],等待状态结束,继续运行... 当前是线程[Thread-10],等待状态结束,继续运行... 当前是线程[Thread-9],等待状态结束,继续运行... 请输入你要执行的线程数量:1 最终结果: 10 """ 上下文管理.format(obj.getName())) with cond: .format(obj.getName())) cond.wait() 暂停,等待唤醒 num += 1 .format(obj.getName())) 实例化递归锁对象 )) with cond: cond.notify(task_num) 通知唤醒 """ Event事件锁方法大全
使用方式
threading .format(obj.getName())) .format(obj.getName())) event.wait() 暂停,等待绿灯通行 : event = threading.Event() 实例化事件锁对象 等待CPU调度执行 event.set() 设置为绿灯 针对第一次 event.clear() 设置为红灯,如果没有他那么上面不管wait()多少次都没用了。因为全都是绿灯 event.set() 再次设置为绿灯,针对第二次 """ Semaphore信号量锁方法大全
使用方式
threading time task(): sema.acquire() time.sleep(1) obj =.format(obj.getName())) sema.release() : sema = threading.Semaphore(3) 实例化信号量锁对象,代表每次都跑3条。 等待CPU调度执行 """ 上下文管理task(): with sema: time.sleep(1) obj = threading.current_thread() .format(obj.getName())) """ 扩展:练习题Condition条件锁的应用
time li = [] even(): 加偶数""" with cond: 加锁 in range(2,101,2): if len(li) % 2 != 0: li.append(i) cond.notify() notify()并不会立即终止当前线程的执行,而是告诉另一线程。你可以走了,不过得等我wait()之后 cond.wait() 阻塞住,执行另一线程,直到另一线程发送了notify()并且它wait()了之后。 else: cond.wait() li.append(i) cond.notify() : cond.notify() odd(): 加奇数 with cond: in range(1,1)">if len(li) %2 == 0: li.append(i) cond.notify() cond.wait() : cond.notify() : cond = threading.Condition() t1 = threading.Thread(target=odd) t2 = threading.Thread(target=even) t1.start() t2.start() t1.join() t2.join() print(li) Event事件锁的应用
libai(): event.wait() 李白:老杜啊,不喝了我喝不下了!) event.set() event.clear() event.wait() 李白:呼呼呼...睡着了..) dufu(): 杜甫:老李啊,来喝酒!) event.set() event.clear() event.wait() 杜甫:老李啊,再来一壶?) 杜甫:...老李?) event.set() : event = threading.Event() t1 = threading.Thread(target=libai) t2 = threading.Thread(target=dufu) t1.start() t2.start() t1.join() t2.join() 扩展:锁的关系浅析这里我们来聊一聊锁的关系。 我们可以看一下递归锁的源码: def __init__(self): self._block = _allocate_lock() self._owner = None self._count = 0 计数器 而 __init__(self,lock=None): if lock is None: lock = RLock() 可以看到条件锁的内部是基于递归锁,而递归锁又是基于同步锁来做的 self._lock = lock self.acquire = lock.acquire self.release = lock.release try: self._release_save = lock._release_save except AttributeError: pass : self._acquire_restore = lock._acquire_restore : self._is_owned = lock._is_owned pass self._waiters = _deque() class Event: (self): self._cond = Condition(Lock()) 实例化出了一个条件锁。 self._flag = False _reset_internal_locks(self): private! called by Thread._reset_internal_locks by _after_fork() self._cond.(Lock()) is_set(self): Return true if and only if the internal flag is true.""" return self._flag isSet = is_set Semaphore: ): if value < 0: raise ValueError(semaphore initial value must be >= 0) self._cond = Condition(Lock()) 可以看到,这里是实例化出了一个条件锁 self._value = value (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |