Python阻塞堆栈多线程
我正在学习python中的多线程,我试图实现的是一个堆栈类,它追加(v)等待直到大小未满,以及一个pop()等待直到堆栈不为空。我的实现如下,它看起来正确吗Python阻塞堆栈多线程,python,multithreading,Python,Multithreading,我正在学习python中的多线程,我试图实现的是一个堆栈类,它追加(v)等待直到大小未满,以及一个pop()等待直到堆栈不为空。我的实现如下,它看起来正确吗 import threading class BlockingStack(object): def __init__(self,max_size=1000): self.max_size = max_size self.stack = [] self.notifynonempty =
import threading
class BlockingStack(object):
def __init__(self,max_size=1000):
self.max_size = max_size
self.stack = []
self.notifynonempty = threading.Condition()
self.notifynotfull = threading.Condition()
def append(self,v):
self.notifynonempty.acquire()
while len(self.stack) == self.max_size:
self.notifynotfull.wait()
self.stack.append(v)
if len(self.stack) == 1:
self.notifynonempty.notify()
self.notifynonempty.release()
def pop(self):
self.notifynotfull.acquire()
while len(self.stack) == 0:
self.notifynonempty.wait()
v = self.stack.pop()
if len(self.stack)==self.max_size - 1:
self.notifynotfull.notify()
self.notifynotfull.release()
return v
追加中的第一个问题如下:
self.notifynonempty.acquire()
当您要在另一个同步对象上阻塞或将要死锁时,切勿获取
条件并保持它。将其移动到其他条件的等待
成功后
下一步:
试图在你没有获得的条件下等待,这是一场比赛,大致相当于一开始就没有条件。在循环之前,您需要获取该条件
此外,如果任何地方出现异常,您将泄漏锁,这将使您的程序死锁。您可以通过仔细的尝试
/最终
工作来解决这个问题,但是只使用with
语句要简单得多,如文档中所示
因此:
pop
的问题与此相反
但是,对于大多数使用模式,让两个条件共享相同的互斥将更有效,如下所示:
self.mutex = threading.Lock()
self.notifynonempty = threading.Condition(self.mutex)
self.notifynotfull = threading.Condition(self.mutex)
import queue
class Stack(queue.Queue):
def _init(self, maxsize):
self.queue = []
def _qsize(self):
return len(self.queue)
def _put(self, item):
self.queue.append(item)
def _get(self):
return self.queue.pop()
…这也让事情变得更简单
另外,不需要在notify()
之前进行检查。在某些情况下(只有一个生产者,或者只有一个消费者),它可能会使事情变得更有效,但它可能会使其他情况变得更慢,使事情变得更复杂,并且在调试过程中更难检测到竞争。所以,我暂时不谈了。一旦一切正常,就用实际使用模式编写一些基准测试,并用两种方法进行测试(当然,只使用附加检查和pop检查)
作为旁注,如果您这样做不是为了了解条件,而是因为您需要一个自同步堆栈,那么已经提供了这一点
即使没有,你也可以这样写:
self.mutex = threading.Lock()
self.notifynonempty = threading.Condition(self.mutex)
self.notifynotfull = threading.Condition(self.mutex)
import queue
class Stack(queue.Queue):
def _init(self, maxsize):
self.queue = []
def _qsize(self):
return len(self.queue)
def _put(self, item):
self.queue.append(item)
def _get(self):
return self.queue.pop()
关于这一点,现在我想起来了,文档链接到,它应该提供可靠的示例代码。它比您需要的要复杂一点,所有的block-vs.-try-vs.-timeout选项,以及整个task\u-done
/join
功能,但它仍然非常清晰易读。感谢您的回答,我还有一个问题,notifyNoneEmpty和notifynonfull使用相同的底层锁对象时。如果我们在pop()为空时调用它,然后另一个线程调用append(),这会是一个死锁吗?我想我从python文档中得到了答案,wait方法释放底层线程lock@DDD是,wait
释放基础锁,然后在唤醒(或超时)时重新请求。因此,您必须在等待之前获得基础锁。(另外,释放、睡眠、唤醒和获取是原子的,这一点很重要;这就是为什么不能用事件来代替条件。)
import queue
class Stack(queue.Queue):
def _init(self, maxsize):
self.queue = []
def _qsize(self):
return len(self.queue)
def _put(self, item):
self.queue.append(item)
def _get(self):
return self.queue.pop()