Несколько процессов Python, потребляющих / повторяющихся по одному генератору (разделяй и властвуй) - PullRequest
0 голосов
/ 15 марта 2019

У меня есть генератор Python, который возвращает множество элементов, например:

import itertools

def generate_random_strings():
    chars = "ABCDEFGH"
    for item in itertools.product(chars, repeat=10):
        yield "".join(item)

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

my_strings = generate_random_strings()
for string in my_strings:
    # do something with string...
    print(string)

Это прекрасно работает, я получаю все свои строки, но это медленно.Я хотел бы использовать возможности многопроцессорной обработки Python для «разделяй и властвуй» этого цикла for.Однако, конечно, я хочу, чтобы каждая строка обрабатывалась только один раз.Хотя я нашел много документации по многопроцессорности, я пытаюсь найти самое простое решение для этого с наименьшим количеством кода.Я предполагаю, что каждый поток должен каждый раз брать большой кусок элементов и обрабатывать их, прежде чем вернуться и получить еще один большой кусок и т.д. ...

Большое спасибо,

Ответы [ 4 ]

0 голосов
/ 15 марта 2019

Как упомянул @Hele, лучше всего использовать asyncio, вот пример

Код

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# python 3.7.2

from asyncio import ensure_future, gather, run
import random

alphabet = 'ABCDEFGH'
size = 1000


async def generate():
    tasks = list()
    result = None

    for el in range(1, size):
        task = ensure_future(generate_one())
        tasks.append(task)

        result = await gather(*tasks)

    return list(set(result))


async def generate_one():
    return ''.join(random.choice(alphabet) for i in range(8))


if __name__ == '__main__':

    my_strings = run(generate())

    print(my_strings)

Выход

['CHABCGDD', 'ACBGAFEB', ...

Конечно, вам нужно улучшить generate_one, этот вариант очень медленный.

0 голосов
/ 15 марта 2019

Вы можете использовать multiprocessing.

import multiprocessing

def string_fun(string):
    # do something with string...
    print(string)

my_strings = generate_random_strings()
num_of_threads = 7
pool = multiprocessing.Pool(num_of_threads)
pool.map(string_fun, my_strings)
0 голосов
/ 15 марта 2019

Предполагая, что вы используете последнюю версию Python, вы можете прочитать кое-что о модуле asyncio.Многопоточность реализовать нелегко из-за блокировки GIL: "В CPython глобальная блокировка интерпретатора, или GIL, является мьютексом, который защищает доступ к объектам Python, не позволяя нескольким потокам одновременно выполнять байт-коды Python. Эта блокировка необходимаглавным образом потому, что управление памятью в CPython не является поточно-ориентированным. "

Таким образом, вы можете переключиться на многопроцессорность или, как сообщалось выше, взглянуть на модуль asycio.
asyncio - AsynchronousI / O> https://docs.python.org/3/library/asyncio.html

Я интегрирую этот ответ с некоторым кодом как можно скорее.
Надеюсь, это поможет,
Hele

0 голосов
/ 15 марта 2019

Самое простое решение с наименьшим количеством кода? многопроцессорный менеджер контекста.

Я предполагаю, что вы можете поместить "сделать что-нибудь со строкой" в функцию с именем "do_something"

from multiprocessing import Pool as ProcessPool

number_of_processes = 4

with ProcessPool(number_of_processes) as pool:
    pool.map(do_something, my_strings)

Если вы хотите снова получить результаты do_something, просто!

with ProcessPool(number_of_processes) as pool:
    results = pool.map(do_something, my_strings)

Вы получите их в списке.

Multiprocessing.dummy - это синтаксическая оболочка для пулов процессов, которая позволяет вам использовать многопроцессорный синтаксис. Если вам нужны потоки вместо процессов, просто сделайте это:

from multiprocessing.dummy import Pool as ThreadPool
...