Как предотвратить одновременную работу библиотеки фьючерсов над ранее повторяющимися элементами? - PullRequest
0 голосов
/ 05 ноября 2018

У меня есть список (lst1) числовых идентификаторов (примерно 300 тыс. Идентификаторов), которые я передаю в API и добавляю результаты API в другой список (lst), например:

lst = []
lst1 = [1,2,3,4,5,6]

print(len(lst1))
counter = 0
for i in lst1:
    url = 'url.com/Id={}'.format(i)
    while True:
        try:
            xml_data1 = requests.get(url).text
            counter = counter+ 1
            print(counter)
            #print(xml_data1)
            break
        except requests.exceptions.RequestException as e:
            print(e)
    lst.append(xml_data1)

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

Код для применения библиотеки futures.concurrent:

def get_data(xml):
    print(len(lst1))
    #counter = 0
    for i in lst1:
        url = 'url.com/Id={}'.format(i)
        while True:
            try:
                xml_data1 = requests.get(url).text
                counter = counter+ 1
                print(counter)
                #print(xml_data1)
                break
            except requests.exceptions.RequestException as e:
                print(e)
        lst.append(xml_data1)

with futures.ThreadPoolExecutor() as executor:  
    df_list = executor.map(get_data, lst1)

редактирование:

def get_data(xml):
    #counter = 0
    for i in lst1:
        url = 'url.com/Id={}'.format(i)
        while True:
            try:
                xml_data1 = requests.get(url).text
                counter = next(counter_object)
                print(counter)
                #print(xml_data1)
                break
            except requests.exceptions.RequestException as e:
                print(e)
        lst.append(xml_data1)
    return lst
with futures.ThreadPoolExecutor() as executor:  
    lst = executor.map(get_data, lst1)

1 Ответ

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

Целые числа неизменны. Таким образом, вы можете сделать свой счетчик глобальным, используя

global counter

Вы также можете определить глобальный counter объект (не целое число), используя itertools.count

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

import itertools
counter_object = itertools.count()  # default: starts at 0

сейчас:

counter = counter+ 1

становится:

counter = next(counter_object)

И значения между рабочими потоками не будут одинаковыми.

Это зависит от того факта, что CPython имеет глобальную блокировку интерпретатора, которая делает работу безопасной. Если вы не используете CPython, вы должны использовать механизмы блокировки потоков, чтобы защитить объект от одновременных изменений.

другая проблема заключается в том, что get_data должен возвращать не список, а элемент. Пусть executor.map создаст список (и ваш цикл бесполезен / вреден, поскольку он увеличивает количество вычислений)

Подведем итог:

def get_data(xml):
    url = 'url.com/Id={}'.format(xml)
    while True:
        try:
            xml_data1 = requests.get(url).text
            counter = next(counter_object)
            print(counter)
            break
        except requests.exceptions.RequestException as e:
            print(e)
    return xml_data1

наконец, executor.map сделан для повторения. Чтобы создать список, вам нужно заставить итерировать его:

with futures.ThreadPoolExecutor() as executor:  
    df_list = list(executor.map(get_data, lst1))
...