看文档发现Python是借鉴Java的多线程,学学java还是有用的。
Lock和RLock的区别
- RLock 叫做可重入锁(reentrant lock),在锁定状态下,必定有线程拥有这把锁, 在未锁定状态下,没有线程拥有该锁。因为线程拥有,才可以继续上锁
- Lock 不能被线程拥有
- RLock除了拥有lock 和unlock状态,还拥有the concepts of “owning thread” and “recursion level”
- 都支持with语句
In [1]: import time
...: from threading import Thread, Lock, current_thread
...:
...: lock = Lock()
...:
...:
...: def xx():
...: lock.acquire()
...: print(current_thread().getName() + " 子线程上锁")
...: while 1:
...: if not lock.locked():
...: print(current_thread().getName() + " 回到子线程")
...: break
...: Thread(target=xx).start()
...: time.sleep(2)
...: print(current_thread().getName() + " 释放锁")
...: lock.release()
Thread-335 子线程上锁
MainThread 释放锁
Thread-335 回到子线程
上面看出lock在子线程上锁然后被主线程释放,因此证明Lock是不被线程拥有的
In [3]: import time
...: from threading import Thread, Lock, RLock, current_thread
...:
...: lock = RLock()
...: lock.acquire()
...:
...: def xx():
...: lock.release()
...: print(current_thread().getName() + " 子线程上锁")
...: while 1:
...: print(current_thread().getName() + " 回到子线程")
...: break
...:
...:
...: Thread(target=xx).start()
...: time.sleep(2)
...: print(current_thread().getName() + " 释放锁")
Exception in thread Thread-464:
Traceback (most recent call last):
File "c:\users\zhao\miniconda3\lib\threading.py", line 917, in _bootstrap_inner
self.run()
File "c:\users\zhao\miniconda3\lib\threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "<ipython-input-3-9016daf20cf0>", line 8, in xx
lock.release()
RuntimeError: cannot release un-acquired lock
上面看出Rlock如果尝试其他线程释放,会报错。
wait/notify 使用
-
这两个都是用在Condition Objects,需要注意的是Condition Objects需要锁,锁是条件对象的一部分。你可以传入锁也可以默认创建。
-
锁必须是Lock或RLock,默认使用RLock
-
wait是临时释放锁,并处于等待状态,其他线程可以获得锁。
-
wait 必须再次获得锁才能往下运行
-
notify不释放锁
-
其他线程获得同一个锁后,调用notify,wait可以去进行锁的竞争
import threading
cv = threading.Condition()
def xx():
with cv:
print("xx 1")
cv.wait()
print("xx 2")
def vv():
with cv:
print("vv 1")
cv.notify()
print("vv 2")
k = threading.Thread(target=xx)
v = threading.Thread(target=vv)
k.start()
v.start()
# 第一种情况
# xx 1
# vv 1
# vv 2
# xx 2
# 第二种情况
# vv 1
# vv 2
# xx 1
# 代码等待中
上面代码只有两种方式,当xx函数先执行,是第一种情况,当vv函数先执行,是第二种情况。仔细分析跟上述观点一致。
生产者消费者模式
这段代码是抄自Python标准库Threading源代码中,使用wait/notify实现线程安全的容器
import sys as _sys
from time import sleep as _sleep
from collections import deque as _deque
from threading import RLock, Condition, Thread, currentThread
def _test():
class BoundedQueue():
def __init__(self, limit):
self.mon = RLock()
self.rc = Condition(self.mon)
self.wc = Condition(self.mon)
self.limit = limit
self.queue = _deque()
def _note(self, format, *args):
format = format % args
name = currentThread().getName()
format = "%s: %s\n" % (name, format)
_sys.stderr.write(format)
def put(self, item):
self.mon.acquire()
while len(self.queue) >= self.limit:
self._note("put(%s): queue full", item)
print(self.mon._RLock__count)
self.wc.wait()
print(self.mon._RLock__count)
self.queue.append(item)
self._note("put(%s): appended, length now %d",
item, len(self.queue))
self.rc.notify()
self.mon.release()
def get(self):
self.mon.acquire()
while not self.queue:
self._note("get(): queue empty")
self.rc.wait()
item = self.queue.popleft()
self._note("get(): got %s, %d left", item, len(self.queue))
self.wc.notify()
self.mon.release()
return item
class ProducerThread(Thread):
def __init__(self, queue, quota):
Thread.__init__(self, name="Producer")
self.queue = queue
self.quota = quota
def run(self):
from random import random
counter = 0
while counter < self.quota:
counter = counter + 1
self.queue.put("%s.%d" % (self.name, counter))
_sleep(random() * 0.00001)
class ConsumerThread(Thread):
def __init__(self, queue, count):
Thread.__init__(self, name="Consumer")
self.queue = queue
self.count = count
def run(self):
while self.count > 0:
item = self.queue.get()
print item
self.count = self.count - 1
NP = 1
QL = 1
NI = 5
Q = BoundedQueue(QL)
P = []
for i in range(NP):
t = ProducerThread(Q, NI)
t.name = ("Producer-%d" % (i + 1))
P.append(t)
C = ConsumerThread(Q, NI * NP)
for t in P:
t.start()
_sleep(0.000002)
C.start()
for t in P:
t.join()
C.join()
if __name__ == '__main__':
_test()
网友评论