asyncio с несколькими процессорами [дубликат]

Если вы не используете jQuery в своем коде, этот ответ для вас

Ваш код должен быть чем-то вроде этого:

function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // always ends up being 'undefined'

Феликс Клинг отлично справился с написанием ответа для людей, использующих jQuery для AJAX, я решил предоставить альтернативу для людей, которые этого не делают.

( Примечание. используя новый API fetch, угловые или обещания, я добавил еще один ответ ниже )


То, с чем вы столкнулись

Это краткое резюме «Объяснение проблемы» из другого ответа, если вы не уверены, прочитав это, прочитайте это.

A в AJAX означает асинхронность. Это означает, что отправка запроса (или, скорее, получение ответа) вынимается из обычного потока выполнения. В вашем примере .send немедленно возвращается, а следующий оператор return result; выполняется до того, как функция, которую вы передали, когда был вызван обратный вызов success.

Это означает когда вы возвращаетесь, слушатель, который вы определили, еще не выполнил, что означает, что возвращаемое вами значение не было определено.

Вот простая аналогия

function getFive(){ 
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

(Fiddle)

Возвращаемое значение a - undefined так как часть a=5 еще не выполнена. AJAX действует так, вы возвращаете значение до того, как сервер получил возможность сообщить вашему браузеру, что это за значение.

Одним из возможных решений этой проблемы является код повторно активно , сообщая вашей программе, что делать, когда расчет завершен.

function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){ 
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}

Это называется CPS . В основном, мы передаем getFive действие, которое необходимо выполнить, когда оно завершается, мы сообщаем нашему кодексу, как реагировать, когда событие завершается (например, наш вызов AJAX или в этом случае время ожидания).

Использование будет:

getFive(onComplete);

Который должен предупредить «5» на экране. (Fiddle) .

Возможные решения

Существуют два способа решения этой проблемы:

  1. Сделать AJAX синхронный вызов (позволяет называть его SJAX).
  2. Реструктурируйте свой код для правильной работы с обратными вызовами.

1. Синхронный AJAX - Не делайте этого !!

Что касается синхронного AJAX, не делайте этого! Ответ Феликса вызывает некоторые веские аргументы в пользу того, почему это плохая идея. Подводя итог, он заморозит браузер пользователя, пока сервер не вернет ответ и не создаст очень плохой пользовательский интерфейс. Вот еще краткое резюме из MDN о том, почему:

XMLHttpRequest поддерживает как синхронную, так и асинхронную связь. В общем, однако, асинхронные запросы должны быть предпочтительнее синхронных запросов по причинам производительности.

Короче говоря, синхронные запросы блокируют выполнение кода ... ... это может вызвать серьезные проблемы ...

blockquote>

Если вы имеете , вы можете передать флаг: Вот как это сделать:

var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {// That's HTTP for 'ok'
  console.log(request.responseText);
}

2. Код реструктуризации

Пусть ваша функция принимает обратный вызов. В примере код foo может быть сделан для принятия обратного вызова. Мы сообщим нашему кодексу, как отреагировали , когда foo завершает работу.

Итак:

var result = foo();
// code that depends on `result` goes here

Становится:

foo(function(result) {
    // code that depends on `result`
});

Здесь мы передали анонимную функцию, но мы могли бы так же легко передать ссылку на существующую , чтобы он выглядел следующим образом:

function myHandler(result) {
    // code that depends on `result`
}
foo(myHandler);

Для получения дополнительной информации о том, как выполняется этот вид обратного вызова, проверьте ответ Felix.

Теперь давайте определим сам foo, чтобы действовать соответственно

function foo(callback) {
    var httpRequest = new XMLHttpRequest();
    httpRequest.onload = function(){ // when the request is loaded
       callback(httpRequest.responseText);// we're calling our method
    };
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
}

(скрипка)

Теперь мы сделали нашу функцию foo принять действие, которое будет выполняться, когда AJAX завершится успешно, мы можем продолжить это, проверив, не является ли статус ответа не 200 и действует соответственно (создайте обработчик сбоя и т. д.). Эффективное решение нашей проблемы.

Если вам все еще трудно понять это , прочитайте руководство по началу работы AJAX в MDN.

41
задан dano 19 March 2015 в 21:57
поделиться

3 ответа

Вы должны быть в состоянии безопасно объединить asyncio и multiprocessing без особых проблем, хотя вы не должны использовать multiprocessing напрямую. Кардинальный грех asyncio (и любой другой асинхронной структуры на основе цикла событий) блокирует цикл события. Если вы попытаетесь напрямую использовать multiprocessing, каждый раз, когда вы блокируете дождевой процесс, вы собираетесь заблокировать цикл событий. Очевидно, что это плохо.

Самый простой способ избежать этого - использовать BaseEventLoop.run_in_executor для выполнения функции в concurrent.futures.ProcessPoolExecutor . ProcessPoolExecutor - пул процессов, реализованный с использованием multiprocessing.Process, но asyncio имеет встроенную поддержку для выполнения в нем функции без блокировки цикла события. Вот простой пример:

import time
import asyncio
from concurrent.futures import ProcessPoolExecutor

def blocking_func(x):
   time.sleep(x) # Pretend this is expensive calculations
   return x * 5

@asyncio.coroutine
def main():
    #pool = multiprocessing.Pool()
    #out = pool.apply(blocking_func, args=(10,)) # This blocks the event loop.
    executor = ProcessPoolExecutor()
    out = yield from loop.run_in_executor(executor, blocking_func, 10)  # This does not
    print(out)

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Для большинства случаев эта функция сама по себе достаточно хороша. Если вам понадобятся другие конструкции из multiprocessing, например Queue, Event, Manager и т. Д., Существует сторонняя библиотека, называемая aioprocessing (полное раскрытие: Я написал это), который предоставляет asyncio -совместимые версии всех структур данных multiprocessing. Вот пример демонстрации того, что:

import time
import asyncio
import aioprocessing
import multiprocessing

def func(queue, event, lock, items):
    with lock:
        event.set()
        for item in items:
            time.sleep(3)
            queue.put(item+5)
    queue.close()

@asyncio.coroutine
def example(queue, event, lock):
    l = [1,2,3,4,5]
    p = aioprocessing.AioProcess(target=func, args=(queue, event, lock, l)) 
    p.start()
    while True:
        result = yield from queue.coro_get()
        if result is None:
            break
        print("Got result {}".format(result))
    yield from p.coro_join()

@asyncio.coroutine
def example2(queue, event, lock):
    yield from event.coro_wait()
    with (yield from lock):
        yield from queue.coro_put(78)
        yield from queue.coro_put(None) # Shut down the worker

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    queue = aioprocessing.AioQueue()
    lock = aioprocessing.AioLock()
    event = aioprocessing.AioEvent()
    tasks = [ 
        asyncio.async(example(queue, event, lock)),
        asyncio.async(example2(queue, event, lock)),
    ]   
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
50
ответ дан dano 24 August 2018 в 18:17
поделиться

См. PEP 3156, в частности раздел о взаимодействии потоков:

http://www.python.org/dev/peps/pep-3156/#thread-interaction

В этом документе четко указаны новые методы асинхронного использования, в том числе run_in_executor (). Обратите внимание, что Executor определен в concurrent.futures, я предлагаю вам также посмотреть там.

1
ответ дан Glenn 24 August 2018 в 18:17
поделиться

Да, есть несколько бит, которые могут (или не могут) укусить вас.

  • Когда вы запускаете что-то вроде asyncio, он ожидает запустить один поток или процесс. Это само по себе не работает с параллельной обработкой. Вы каким-то образом должны распределять работу, оставляя операции ввода-вывода (в частности, в сокетах) в одном потоке / процессе.
  • Хотя ваша идея передать отдельные подключения другому процессу обработчика, это приятно, это трудно реализовать. Первым препятствием является то, что вам нужно вытащить соединение из asyncio, не закрывая его. Следующим препятствием является то, что вы не можете просто отправить файловый дескриптор другому процессу, если вы не используете конкретный для платформы (возможно, Linux) код из C-расширения.
  • Обратите внимание, что модуль multiprocessing известен создайте несколько потоков для связи. В большинстве случаев, когда вы используете структуры связи (например, Queue s), создается поток. К сожалению, эти потоки не полностью невидимы. Например, они могут не сработать чисто (когда вы намерены прекратить свою программу), но в зависимости от их количества использование ресурсов может быть заметным само по себе.

Если вы действительно собираетесь обрабатывать отдельные соединения в отдельных процессах, я предлагаю изучить различные подходы. Например, вы можете поместить сокет в режим прослушивания, а затем одновременно принимать соединения из нескольких рабочих процессов параллельно. Как только рабочий закончит обработку запроса, он может принять следующее соединение, поэтому вы все равно используете меньше ресурсов, чем форматирование процесса для каждого соединения. Spamassassin и Apache (mpm prefork) могут использовать эту рабочую модель, например. Это может оказаться проще и надежнее в зависимости от вашего варианта использования. В частности, вы можете заставить своих работников умереть после обслуживания настроенного количества запросов и быть вызванными мастер-процессом, тем самым устраняя большую часть негативных последствий утечек памяти.

3
ответ дан Helmut Grohne 24 August 2018 в 18:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: