条件变量没有等待两个线程

时间:2018-02-20 09:49:29

标签: python multithreading python-3.x sockets asyncore

难以接受我遇到的这个线程同步。 基本上,我正在写一个out缓冲区,并等待一个条件变量,直到读取缓冲区填充了来自套接字的响应。这是一个非常简单的线程同步。

def write_wait_response(self, buffer, timeout=30):
        '''
            Write and wait for response
            Params:
                Buffer BYTE encoded data
                Timeout timeout to wait for response
            Returns: 
                response str if successful
        '''
        self.buffer = buffer

        if self.waitLock(timeout):
            # condition var was signaled, we can return a response
            readbuf = bytes(self.readbuffer)
            self.readbuffer = b''
            return readbuf
        else:
            print("AsyncClientSocket: No response recieved from {} in {} seconds, dumping buffer".format(
                    self.sa, timeout))
            self.buffer = ''
            raise TimeoutError("AsyncClientSocket Timed Out")

def handle_read(self):
        self.readbuffer, address = self.recvfrom(2048)
        print(self.readbuffer)
        print("notifying")
        self.cond.notifyAll() 

看起来很简单,对吧?有1个线程正在等待条件变量,1个线程(asyncore异步回调循环)将填充self.readbuffer并通知条件变量。更奇怪的是:如果我执行time.sleep()而不是使用条件变量,我会在write_wait_response()的调用线程上获得一个完美填充的self.readbuffer。显然,这不是我能接受的解决方案。

这是我期待的事情:

  1. 调用write_wait_response(缓冲区),写入缓冲区并等待 on self.cond
  2. asyncore回调循环调用handle_write,将字节写入socket。
  3. 服务器接收字节,写入响应。
  4. asyncore回调循环查看套接字上的字节,读入 self.readbuffer,通知cv
  5. ?????????? write_wait_response应该取消阻止吗?
  6. 控制台输出:

    waiting <- thread 1 waiting on CV
    AsyncClientSocket: writing 5 bytes <- thread 2: handle_write
    b'200,2' <- thread 2: that's the server response
    notifying <- thread 2: that's handle_read attempting to notify the held CV
    
    error: uncaptured python exception, closing channel <my_socket_stuff.AsyncClientSocket connected 127.0.0.1:50000 at 0x1051bf438> (<class 'RuntimeError'>:cannot notify on un-acquired lock
    

    注意:在此日志的末尾,线程1仍在等待self.cond。发生了什么事?

    全班:

    class AsyncClientSocket(asyncore.dispatcher):
        def __init__(self, socketargs):
            asyncore.dispatcher.__init__(self)
            family, type, proto, canonname, sa = socketargs
            self.sa = sa
            self.create_socket(family, type)
    
            if type == socket.SOCK_STREAM:
                self.connect( sa )
            elif type == socket.SOCK_DGRAM:
                pass
    
            self.buffer = b''
            self.lock = threading.Lock()
            self.cond = threading.Condition(self.lock)
            self.readbuffer = b''
    
        def write_wait_response(self, buffer, timeout=30):
            '''
                Write and wait for response
                Params:
                    Buffer BYTE encoded data
                    Timeout timeout to wait for response
                Returns: 
                    response str if successful
            '''
            self.buffer = buffer
    
            if self.waitLock(timeout):
                # condition var was signaled, we can return a response
                readbuf = bytes(self.readbuffer)
                self.readbuffer = b''
                return readbuf
            else:
                print("AsyncClientSocket: No response recieved from {} in {} seconds, dumping buffer".format(
                        self.sa, timeout))
                self.buffer = ''
                raise TimeoutError("AsyncClientSocket Timed Out")
    
        def waitLock(self, timeout):
            '''
                Wait for timeout seconds on CV
            '''
            try:
                self.cond.acquire()
                print("waiting")
                return self.cond.wait(timeout)
            finally:
                self.cond.release()
    
    
        def handle_connect(self):
            pass
    
        def handle_close(self):
            self.close()
    
        def handle_read(self):
            self.readbuffer, address = self.recvfrom(2048)
            print(self.readbuffer)
            print("notifying")
            self.cond.notifyAll() 
    
        def writable(self):
            return (len(self.buffer) > 0)
    
        def handle_write(self):
            print("AsyncClientSocket: writing {} bytes".format(len(self.buffer)))
            self.readbuffer = b''
            sent = self.sendto(self.buffer, self.sa)
            self.buffer = self.buffer[sent:]
    

1 个答案:

答案 0 :(得分:1)

想出来。这与asyncore无关。我只是错误地发出了条件变量的信号。 The python3 threading api doc says the calling thread of notify() must acquire the underlying lock这是有道理的,不希望两个生成器通知相同的条件变量。希望在对方执行任务时阻止关键部分。

def handle_read(self):
    try:
        self.cond.acquire()
        self.readbuffer, address = self.recvfrom(2048)
        print(self.readbuffer)
        self.cond.notify() 
    finally:
        self.cond.release()