2016-01-27 4 views
4

Я пытаюсь написать небольшой параллельный обработчик потока с помощью Slack RTM API, и мне интересно, является ли это наиболее эффективным использование сопрограмм Python. В пакете asyncio имеется множество опций, но трудно определить, какой правильный подход для проекта и документация, на мой взгляд, не очень помогают объяснить, какие преимущества/недостатки для каждого.Является ли это предполагаемым шаблоном для использования сопрограмм Python `asyncio`?

Я думаю, что мне не нужно иметь накладные расходы на несколько потоков здесь, и мне нужна связь между асинхронными циклами. Должен ли я создать отдельный BaseEventLoop для каждой из моих функций?

Будучи Python, я думаю, что есть около -deterministic здесь ответа на этот вопрос (There should be one-- and preferably only one --obvious way to do it), но я боюсь, что добавление всего это асинхронный хлама может просто сделать мой код меньшей производительности, чем полностью последовательная наивной реализация.

# Is this the best way to communicate between coroutines? 
incoming_message_q = asyncio.Queue() 

async def print_event(): 
    logging.info("Entering log loop") 

    # Should this operate within it's own BaseEventLoop? 
    while True: 
     event = await incoming_message_q.get() 
     logging.info(event) 

async def log_queue_status(): 
    while True: 
     logging.info(incoming_message_q.qsize()) 
     await asyncio.sleep(5) 

async def read_rtm_connection(client, q): 
    if client.rtm_connect(): 
     logging.info("Successful Slack RTM connection") 
     while True: 

      # How do I make this part non-blocking? 
      events = client.rtm_read() 

      for event in events: 
       logging.info("Putting onto the queue", event) 
       if event["type"] == "presence_change": 
        await q.put(event) 
       elif event["type"] == "message": 
        await q.put(event) 
       else: 
        logging.info("Not sure what to do") 

      await asyncio.sleep(0.1) 
    else: 
     logging.info("RTM connection failed.") 

loop = asyncio.get_event_loop() 
loop.create_task(print_event()) 
loop.create_task(log_queue_status()) 
loop.create_task(read_rtm_connection(client, incoming_message_q)) 
loop.run_forever() 
+0

Я не знаком с Slack или каким-то методом rtm_read(), поэтому с учетом этого: Чтобы сделать что-то неблокирующее, это должна быть ожидаемая сопрограмма, которую вы можете запускать как задачу asyncio, используя security_future() , Если rtm_read() не является сопрограммой coroutine, вы можете попробовать обернуть ее в сопрограмму, которая каким-то образом опросит rtm_read(), чтобы выполнить доставку новых событий и передать их другим методам. В зависимости от вашего случая вы можете передавать сообщения о событиях прямо в очередь или иным образом возвращать будущее каждый раз, когда принимается событие, и использовать внутрипрограммный цикл внутри сопрограммы для их итерации. – shongololo

ответ

1

Если вы хотите, чтобы взаимодействовать с слабину в качестве asyncio дружественным способом, вы будете должны использовать не блокирующий API. Я не уверен, что вы в настоящее время используете, но если он не содержит каких-либо asyncio сопрограмм, это, вероятно, не будет легко интегрироваться в asyncio, если вы не запустите все блокирующие вызовы в фоновом потоке через loop.run_in_executor. Другим вариантом было бы фактически преобразовать все базовые блокирующие вызовы ввода/вывода в библиотеке, чтобы они не блокировались, что обычно представляет собой целую кучу работы.

Хорошей новостью является то, что существует хотя бы одна библиотека, которая уже сделала эту работу для вас; slacker-asyncio, который является вилкой slacker. Вы должны иметь возможность использовать это для взаимодействия с RTM API через сопрограммы.

+0

Спасибо за информацию @dano. Я смотрел на slackhq/'python-slackclient' lib вчера после того, как я разместил это и увидел, что он действительно очень блокирует. Если бы я выполнил эту часть кода в 'loop.run_in_executor', как вы говорите, что было бы безопасным способом связи между сопрограммами и потоком' slackclient'? – dalanmiller

+0

@dalanmiller Если вы просто выполняете методы на объекте 'client', как в приведенном выше примере, вы можете просто сделать' events = loop.run_in_executor (None, client.rtm_read) '. 'asyncio' автоматически вернет результат из' client.rtm_read() '. – dano

+0

Если у меня есть другой 'run_in_executor', хотя и будет, безопасно ли им также обращаться к очередям? AKA с 'run_in_executor' Мне нужно беспокоиться о безопасности потоков да? – dalanmiller

Смежные вопросы