Как вернуть значения из экземпляров Process или Thread? - PullRequest
0 голосов
/ 21 ноября 2018

Итак, я хочу запустить функцию, которая может искать информацию в Интернете или напрямую из моей собственной базы данных mysql.Первый процесс будет трудоемким, второй относительно быстрым.

Имея это в виду, я создаю процесс, который запускает этот составной поиск (find_compound_view).Если процесс завершается относительно быстро, это означает, что он присутствует в базе данных, поэтому я могу немедленно отобразить результаты.В противном случае я буду отображать «drax_retrieving_data.html».

Глупое решение, которое я придумал, состояло в том, чтобы дважды запустить функцию, один раз, чтобы проверить, занимает ли процесс много времени, другой, чтобы фактически получить возвращаемые значения функции.Это в значительной степени потому, что я не знаю, как вернуть значения моей функции find_compound_view.Я пробовал поискать в Google, но не могу найти, как именно вернуть значения из класса Process.

   p = Process(target=find_compound_view, args=(form,))
        p.start()
        is_running = p.is_alive()
        start_time=time.time()
        while is_running:
            time.sleep(0.05)
            is_running = p.is_alive()
            if time.time() - start_time > 10 :
                print('Timer exceeded, DRAX is retrieving info!',time.time() - start_time)
                return render(request,'drax_internal_dbs/drax_retrieving_data.html')
        compound = find_compound_view(form,use_email=False)

   if compound:
      data=*****
      return  render(request, 'drax_internal_dbs/result.html',data)

1 Ответ

0 голосов
/ 22 ноября 2018

Вам понадобится multiprocessing.Pipe или multiprocessing.Queue, чтобы отправить результаты обратно вашему родительскому процессу.Если вы просто делаете I / 0, вы должны использовать Thread вместо Process, так как он более легкий и большую часть времени будет тратить на ожидание.Я покажу вам, как это делается для Process и Threads в целом.


Process with Queue

Многопроцессорная очередь построена поверх трубы идоступ синхронизируется с замками / семафорами.Очереди являются поточно-ориентированными и безопасными для процессов, то есть вы можете использовать одну очередь для нескольких процессов производителя / потребителя и даже для нескольких потоков в этих процессах.Добавление первого элемента в очередь также запустит фидерный поток в вызывающем процессе.Дополнительные издержки multiprocessing.Queue делают использование канала для сценариев одного производителя / одного потребителя предпочтительным и более производительным.

Вот как отправить и получить результат с помощью multiprocessing.Queue:

from multiprocessing import Process, Queue

SENTINEL = 'SENTINEL'

def sim_busy(out_queue, x):
    for _ in range(int(x)):
        assert 1 == 1
    result = x
    out_queue.put(result)
    # If all results are enqueued, send a sentinel-value to let the parent know
    # no more results will come.
    out_queue.put(SENTINEL)


if __name__ == '__main__':

    out_queue = Queue()

    p = Process(target=sim_busy, args=(out_queue, 150e6))  # 150e6 == 150000000.0
    p.start()

    for result in iter(out_queue.get, SENTINEL):  # sentinel breaks the loop
        print(result)

Очередь передается в функцию в качестве аргумента, результаты - .put() в очереди и родительский get.() s из очереди..get() является блокирующим вызовом, выполнение не возобновляется до тех пор, пока не будет получено (указание параметра времени ожидания возможно).Обратите внимание, что sim_busy выполняет здесь интенсивную работу с процессором, тогда вы выбираете процессы поверх потоков.


Process & Pipe

Для одногоОдного соединения достаточно трубы.Настройка практически идентична, просто методы называются по-разному, и вызов Pipe() возвращает два объекта подключения.В дуплексном режиме оба объекта имеют конец для чтения и записи, при duplex=False (симплекс) первый объект соединения является концом чтения канала, а второй - концом записи.В этом базовом сценарии нам просто нужен симплекс-канал:

from multiprocessing import Process, Pipe

SENTINEL = 'SENTINEL'


def sim_busy(write_conn, x):
    for _ in range(int(x)):
        assert 1 == 1
    result = x
    write_conn.send(result)
    # If all results are send, send a sentinel-value to let the parent know
    # no more results will come.
    write_conn.send(SENTINEL)


if __name__ == '__main__':

    # duplex=False because we just need one-way communication in this case.
    read_conn, write_conn = Pipe(duplex=False)

    p = Process(target=sim_busy, args=(write_conn, 150e6))  # 150e6 == 150000000.0
    p.start()

    for result in iter(read_conn.recv, SENTINEL):  # sentinel breaks the loop
        print(result)

Thread & Queue

Для использования с потоками вы хотите переключиться на queue.Queue.queue.Queue строится поверх collections.deque, добавляя некоторые блокировки, чтобы сделать его поточно-ориентированным.В отличие от очереди и канала многопроцессорной обработки объекты, помещенные в queue.Queue, не будут подвергаться травлению.Поскольку потоки используют одно и то же адресное пространство памяти, сериализация для копирования в память не требуется, передаются только указатели.

from threading import Thread
from queue import Queue
import time

SENTINEL = 'SENTINEL'


def sim_io(out_queue, query):
    time.sleep(1)
    result = query + '_result'
    out_queue.put(result)
    # If all results are enqueued, send a sentinel-value to let the parent know
    # no more results will come.
    out_queue.put(SENTINEL)


if __name__ == '__main__':

    out_queue = Queue()

    p = Thread(target=sim_io, args=(out_queue, 'my_query'))
    p.start()

    for result in iter(out_queue.get, SENTINEL):  # sentinel-value breaks the loop
        print(result)

  • Чтение здесь Почему for result in iter(out_queue.get, SENTINEL):следует отдавать предпочтение настройке while True...break, где это возможно.
  • Чтение здесь Почему вы должны использовать if __name__ == '__main__': во всех ваших сценариях и особенно в многопроцессорной.
  • Подробнеео get() -использовании здесь .
...