3

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

run方法使用一个while not self.__stop:语句和一个threading.Condition对象来等待调用被放置在双端队列中,然后调用self.__process_calls。循环的else一部分对. 如果,则在任何尝试从另一个线程调用“可调用”方法之一时引发异常。while__process_callsself.__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()
4

1 回答 1

1
  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()
于 2010-07-30T03:30:02.500 回答