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

标签 python multithreading debugging python-multithreading

我正在开发 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/

相关文章:

python - 推荐的 Python Atom 提要生成器?

python - 什么是 C++ 相当于 'r' 前缀在 Python 中的字符串?

python - 有没有办法自动生成有效的算术表达式?

python - 在python中递归加载doctests

c# - 如何正确使用readerwriterlock

linux - 如何在 Linux 中更改特定线程 (LWT) 的优先级?

java - 我的 getStatement 方法线程安全吗?

sql - 如何找到未封闭的连接?超时时间已到。在操作完成之前超时时间已过或服务器没有响应

c++ - 我可以生成附加到正在运行的调试器的进程吗?

java - 调试Java应用程序IntelliJ时挂起线程