#python #multiprocessing
Вопрос:
Я хотел бы сэкономить время и использовать многопроцессорную обработку для выполнения 10 запросов на получение. До сих пор у меня есть это:
# get one text from the url
def get_one_request_text(url, multiprocessing_queue):
response = requests.get(url)
assert response.status_code == 200
multiprocessing_queue.put(response.text)
# urls is a list of links
def get_many_request_texts(urls):
q = multiprocessing.Queue()
jobs = []
result = []
for url in urls:
p = multiprocessing.Process(target=get_one_request_text, args=(url, q))
jobs.append(p)
p.start()
for p in jobs:
p.join()
for _ in jobs:
result.append(q.get())
return result
if __name__ == '__main__':
# for testing purposes I use the same link
url = 'https://www.imdb.com/name/nm0000138'
urls = [url] * 10 # any number freezes my code, even 1
t1 = time.perf_counter()
texts = get_many_request_texts(urls)
t2 = time.perf_counter()
print(f"Soups: {len(texts)} Execution time: = {round(t2 - t1, 2)} {texts}")
Я ожидаю, что мой скрипт выдаст 10 response.text
в списке, но по какой-то причине моя программа просто зависает, и я ничего не получаю. Даже когда я пытаюсь получить 1 ответ.текст, он зависает.
Что я делаю не так и как я могу получить свой ответ.тексты, используя многопроцессорную обработку, чтобы сэкономить время?
Комментарии:
1. На самом деле он не зависает, но по какой-то причине
p.join()
вызов происходит слишком медленно из-за размера извлеченной страницы. Поместите в очередь (т. е. ) только первые 100 символов страницыmultiprocessing_queue.put(response.text[0:100])
, и вы увидите, что она быстро завершается.2. @qouify Я никогда бы не подумал, что это зависит от длины ответа.текст, спасибо. Тогда я думаю, что это неэффективный способ разбора страниц.
Ответ №1:
Во-первых, это, вероятно, работа, лучше подходящая для многопоточности, а не для многопроцессорной обработки. Во-вторых, независимо от того, используете ли вы многопоточность или многопроцессорность, это было бы легче сделать, если бы вы использовали поток или пул процессов.
Тем не менее, учитывая, что вы делаете то, что делаете, проблема в том, что вы никогда не должны пытаться читать из очереди, в которую записали ваши процессы после того, как вы присоединились к этим процессам. То есть эти процессы все еще должны выполняться для вашего основного процесса для получения этих сообщений. Поэтому вам нужно изменить порядок операций:
def get_many_request_texts(urls):
q = multiprocessing.Queue()
jobs = []
result = []
for url in urls:
p = multiprocessing.Process(target=get_one_request_text, args=(url, q))
jobs.append(p)
p.start()
for _ in jobs:
result.append(q.get())
for p in jobs:
p.join()
return result
См.Раздел Многопроцессорная обработка.Документация по очереди:
Предупреждение Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не использовал
JoinableQueue.cancel_join_thread
), то этот процесс не завершится до тех пор, пока все буферизованные элементы не будут сброшены в канал.Это означает, что при попытке присоединиться к этому процессу может возникнуть тупик, если вы не уверены, что все элементы, помещенные в очередь, были израсходованы. Аналогично, если дочерний процесс не является демоническим, то родительский процесс может приостановить выход при попытке присоединиться ко всем своим недемоническим дочерним элементам.
Обратите внимание, что очередь, созданная с помощью диспетчера, не имеет этой проблемы. См.Руководство по программированию.