gpt4 book ai didi

除非最后一条语句很慢,否则python函数无法返回

转载 作者:行者123 更新时间:2023-11-28 18:55:21 27 4
gpt4 key购买 nike

我正在开发 threading.Thread 的子类,它允许在调用它们的对象所代表的线程中调用和运行其方法,而不是通常的行为。为此,我在目标方法上使用装饰器,将对该方法的调用放在 collections.deque 中,并使用 run 方法处理双端队列。

run 方法使用一个 while not self.__stop: 语句和一个 threading.Condition 对象来等待调用在双端队列中,然后调用 self.__process_callswhile 循环的 else 部分最终调用了 __process_calls。如果 self.__stop,任何从另一个线程调用“可调用”方法之一的尝试都会引发异常。

问题是 __process_calls 无法返回,除非最后一条语句是我在调试期间发现的 print。我试过 a = 1 和显式 return 但都不起作用。尽管将任何 print 语句作为函数的最终语句,它都会返回并且线程不会挂起。知道发生了什么事吗?

编辑:大卫·扎斯拉夫斯基 (David Zaslavsky) 指出打印品之所以有效,是因为它需要一段时间我已经确认

代码有点长,但希望我上面的解释足够清楚,有助于理解它。

import threading
import collections

class BrokenPromise(Exception): pass
class CallableThreadError(Exception): pass
class CallToNonRunningThreadError(CallableThreadError): pass


class Promise(object):
def __init__(self, deque, condition):
self._condition = condition
self._deque = deque

def read(self, timeout=None):
if not self._deque:
with self._condition:
if timeout:
self._condition.wait(timeout)
else:
self._condition.wait()
if self._deque:
value = self._deque.popleft()
del self._deque
del self._condition
return value
else:
raise BrokenPromise

def ready(self):
return bool(self._deque)

class CallableThread(threading.Thread):
def __init__(self, *args, **kwargs):
# _enqueued_calls is used to store tuples that encode a function call.
# It is processed by the run method
self.__enqueued_calls = collections.deque()
# _enqueue_call_permission is for callers to signal that they have
# placed something on the queue
self.__enqueue_call_permission = threading.Condition()
self.__stop = False
super(CallableThread, self).__init__(*args, **kwargs)

@staticmethod
def blocking_method(f):
u"""A decorator function to implement a blocking method on a thread"""
# the returned function enqueues the decorated function and blocks
# until the decorated function# is called and returns. It then returns
# the value unmodified. The code in register runs in the calling thread
# and the decorated method runs in thread that it is called on
f = CallableThread.nonblocking_method_with_promise(f)
def register(self, *args, **kwargs):
p = f(self, *args, **kwargs)
return p.read()
return register

@staticmethod
def nonblocking_method_with_promise(f):
u"""A decorator function to implement a non-blocking method on a
thread
"""
# the returned function enqueues the decorated function and returns a
# Promise object.N The code in register runs in the calling thread
# and the decorated method runs in thread that it is called on.
def register(self, *args, **kwargs):
call_complete = threading.Condition()
response_deque = collections.deque()
self.__push_call(f, args, kwargs, response_deque, call_complete)
return Promise(response_deque, call_complete)
return register

@staticmethod
def nonblocking_method(f):
def register(self, *args, **kwargs):
self.__push_call(f, args, kwargs)
return register

def run(self):
while not self.__stop: # while we've not been killed
with self.__enqueue_call_permission:
# get the condition so that we can wait on it if we need too.
if not self.__enqueued_calls:
self.__enqueue_call_permission.wait()
self.__process_calls()
else:
# if we exit because self._run == False, finish processing
# the pending calls if there are any
self.__process_calls()

def stop(self):
u""" Signal the thread to stop"""
with self.__enqueue_call_permission:
# we do this in case the run method is stuck waiting on an update
self.__stop = True
self.__enqueue_call_permission.notify()

def __process_calls(self):
print "processing calls"
while self.__enqueued_calls:
((f, args, kwargs),
response_deque, call_complete) = self.__enqueued_calls.popleft()
if call_complete:
with call_complete:
response_deque.append(f(self, *args, **kwargs))
call_complete.notify()
else:
f(self, *args, **kwargs)
# this is where you place the print statement if you want to see the
# behavior

def __push_call(self, f, args, kwargs, response_deque=None,
call_complete=None):
if self.__stop:
raise CallToNonRunningThreadError(
"This thread is no longer accepting calls")
with self.__enqueue_call_permission:
self.__enqueued_calls.append(((f, args, kwargs),
response_deque, call_complete))
self.__enqueue_call_permission.notify()


#if __name__=='__main__': i lost the indent on the following code in copying but
#it doesn't matter in this context
class TestThread(CallableThread):
u"""Increment a counter on each call and print the value"""
counter = 0

@CallableThread.nonblocking_method_with_promise
def increment(self):
self.counter += 1
return self.counter

class LogThread(CallableThread):

@CallableThread.nonblocking_method
def log(self, message):
print message

l = LogThread()
l.start()
l.log("logger started")
t = TestThread()
t.start()
l.log("test thread started")
p = t.increment()
l.log("promise aquired")
v = p.read()
l.log("promise read")
l.log("{0} read from promise".format(v))
l.stop()
t.stop()
l.join()
t.join()

最佳答案

  1. __process_calls 正在修改 __enqueued_calls 而不拥有锁。这可能会造成竞争条件。

  2. 编辑:双端队列可能是“线程安全的”(即不会被线程访问破坏),但仍应锁定对其状态的检查。

  3. 停止条件也不安全。

内联评论:

def run(self):        
while not self.__stop: # while we've not been killed
with self.__enqueue_call_permission:
# get the condition so that we can wait on it if we need too.
### should be checking __stop here, it could have been modified before
### you took the lock.
if not self.__enqueued_calls:
self.__enqueue_call_permission.wait()
self.__process_calls()
else:
# if we exit because self._run == False, finish processing
# the pending calls if there are any
self.__process_calls()

关于除非最后一条语句很慢,否则python函数无法返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3368475/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com