Невозможно писать с помощью xlsxwriter, когда я использую многопоточность

Я пытаюсь записать адрес местоположения на свой лист excel, используя пакет xlswriter в python. Проблема, с которой я сталкиваюсь, заключается в том, что когда я пытаюсь запустить функцию с помощью потока, значения не записываются на рабочий лист. См. приведенный ниже код: Здесь выводится список, содержащий все необработанные адреса.

import threading
import xlsxwriter
from geopy.geocoders import Nominatim

geolocator = Nominatim()
outputLocation = 'output.xlsx'
workbook = xlsxwriter.Workbook(outputLocation)
w_sheet4 = workbook.add_worksheet('openstreet')

def openstreet():
    p = 1
    for row in rows:        #rows consists of raw addresses

        try:
            rawAddress = str(" ".join(row[1].strip().split())) #here I am normalizing the addresses using openstreet api
            location = geolocator.geocode(rawAddress)
            w_sheet4.write(p, 1, rawAddress)
            w_sheet4.write(p, 2, str(location.address))
        except Exception as e:
            print "OpenStreet", e

        p += 1

t4 = threading.Thread(target=openstreet)
t4.start()

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

import threading
import xlsxwriter
from geopy.geocoders import Nominatim

geolocator = Nominatim()
outputLocation = 'output.xlsx'
workbook = xlsxwriter.Workbook(outputLocation)
w_sheet4 = workbook.add_worksheet('openstreet')

def openstreet():
    p = 1
    for row in rows:        #rows consists of raw addresses

        try:
            rawAddress = str(" ".join(row[1].strip().split())) #here I am normalizing the addresses using openstreet api
            location = geolocator.geocode(rawAddress)
            w_sheet4.write(p, 1, rawAddress)  #raw address
            w_sheet4.write(p, 2, str(location.address)) #normalize address
        except Exception as e:
            print "OpenStreet", e

        p += 1

#t4 = threading.Thread(target=openstreet)
#t4.start()
openstreet()

Причина, по которой я использую поток, заключается в том, что я использую несколько API (google, yahoo, openstreetmap API, bing), пытаясь нормализовать адреса и сравнить их. Может ли кто-нибудь помочь мне, почему я не могу писать на лист, когда использую поток вместо обычного вызова функции.


person python    schedule 04.08.2015    source источник
comment
Я забыл добавить в свою программу использование threading.join. Я ответил на вопрос ниже.   -  person python    schedule 04.08.2015
comment
Возможно, вам потребуется добавить workbook.close(), чтобы написать книгу.   -  person jmcnamara    schedule 05.08.2015


Ответы (2)


Используйте с осторожностью! XlsxWriter не является потокобезопасным. Я обнаружил, что если вы используете параметр in_memory, он будет использовать SharedStrings и в конечном итоге будет писать в неожиданные ячейки. Я создал билетный банкомат:

обсуждение Github

Обновление от автора XlsxWriter:

Метод _get_shared_string_index() в SharedStrings не является потокобезопасным. Другие методы в этом классе должны быть, поскольку они вызываются только из Workbook (для которого должен быть только один уникальный экземпляр для каждого объекта xlsxwriter) или через деструктор Workbook (когда потоки рабочего листа должны быть объединены и активность остановлена).

я обнаружил, что если я использую параметр in_memory, XlsxWriter будет использовать SharedStrings

Класс SharedStrings можно вызывать независимо от того, используете ли вы in_memory или нет. Параметр in_memory относится к использованию временных файлов, а не SharedStrings (см. документы).

Если вы хотите избежать использования SharedStrings и, таким образом, избежать блокировки, вы можете использовать режим Constant_Memory. Однако есть некоторые оговорки: данные должны быть записаны в порядке строк и столбцов, и вы не можете использовать add_table() или merge_range(). См. документы Contructor (ссылка выше), а также Работа с памятью и производительностью.

И есть ли что-то еще в XlsxWriter, о чем мне нужно беспокоиться?

Возможно, но ничего, что приходит в голову.

person UnstableFractal    schedule 01.03.2016

Я решил это, используя threading.join() в конце функции. Почему нам нужно использовать join(), можно понять из этой ссылки. Код ниже работает для меня.

import threading
import xlsxwriter
from geopy.geocoders import Nominatim

geolocator = Nominatim()
outputLocation = 'output.xlsx'
workbook = xlsxwriter.Workbook(outputLocation)
w_sheet4 = workbook.add_worksheet('openstreet')

def openstreet():
    p = 1
    for row in rows:        #rows consists of raw addresses

        try:
            rawAddress = str(" ".join(row[1].strip().split())) #here I am normalizing the addresses using openstreet api
            location = geolocator.geocode(rawAddress)
            w_sheet4.write(p, 1, rawAddress)
            w_sheet4.write(p, 2, str(location.address))
        except Exception as e:
            print "OpenStreet", e

        p += 1

t4 = threading.Thread(target=openstreet)
t4.start()
t4.join() #we need to use threading.join to make sure the thread function finishes before the main program exits
person python    schedule 04.08.2015
comment
В показанном вами коде нет ничего, что объясняло бы, почему вам нужно вызывать t4.join(), поскольку это должно произойти в любом случае, поскольку t4 не является потоком демона. Обратите внимание, что на самом деле вы не вызываете метод join, потому что в конце нет (). В любом случае, в вашем реальном коде должны происходить другие вещи, не показанные здесь. - person martineau; 04.08.2015