В Python выход из блокировки в DatagramProtocol.datagram_received делает функцию никогда не вызываемой

Я хочу синхронизировать данные между сопрограммами, и в итоге получаю метод, который не вызывается, когда в нем есть "yield".

Чтобы быть более точным, когда я реализую класс DatagramProtocol с методом datagram_received согласно документу ( вдохновленный этим), все работает нормально, я получаю данные. Как только я добавляю "yield" в метод datagram_received, метод больше не вызывается. Вот пример:

loop = asyncio.get_event_loop()
lock = asyncio.Lock(loop=loop)

class MyProtocol(asyncio.DatagramProtocol):
    def datagram_received(self, data, addr):
        global my_data, lock
        print("here")
        # uncomment the following lines and datagram_received is 
        # not called at all (never see the "here" on the console)
        #yield from lock
        #try:
        #    my_data = float(data.decode())
        #finally:
        #    lock.release()

loop.run_until_complete(loop.create_datagram_endpoint(MyProtocol, sock=create_socket(10000)))

loop.run_forever()

Как метод может внезапно перестать вызываться в зависимости от содержимого метода?

Что мне не хватает? Как сделать синхронизацию?

1 ответ

Решение

Что мне не хватает?

Документация, которая вдохновила вас, также гласит:

Сопрограммы могут быть запланированы в методе протокола с использованием sure_future(), но нет никакой гарантии относительно порядка выполнения. Протоколы не знают о сопрограммах, созданных в методах протокола, и поэтому не будут ждать их.

Чтобы иметь надежный порядок выполнения, используйте потоковые объекты в сопрограмме с yield from. Например, сопрограмму StreamWriter.drain() можно использовать для ожидания очистки буфера записи.

Ты не можешь yield from/await внутри datagram_received, ты можешь:

class MyProtocol(asyncio.DatagramProtocol):
    def datagram_received(self, data, addr):
        global my_data, lock
        print("here")
        loop.ensure_future(some_function())

    @asyncio.coroutine
    def some_function(self):
        yield from lock
        try:
            my_data = float(data.decode())
        finally:
            lock.release()

Как метод может внезапно перестать вызываться в зависимости от содержимого метода?

Использование yield или же yield from в функции, делает его генератором. Так datagram_received возвращает объект генератора. Чтобы фактически выполнить код (до выхода), вы должны использовать next, Asyncio делает это с (на основе генератора) сопрограмм (но снова datagram_received не один)

>>> def test():
...     print('test')
...     yield from 'A'
... 
>>> test()
<generator object test at 0x7f4165d42fc0>
>>> next(test())
test
'A'

Подробнее о генераторах: https://jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/

Другие вопросы по тегам