Найти - Пользователи
Полная версия: Excel - работа с одним файлом в несколько потоков
Начало » Python для экспертов » Excel - работа с одним файлом в несколько потоков
1
Ace
Добрый !

Задача: в файле экселя набиты запросы к БД. Запросы идут к нескольким (>100) серверам одновременно. Время ответа от серверов разное.

Что пытаюсь: открыть 1 раз файл экселя, запустить потоки для чтения и выполнения запросов по каждому серверу (1 поток = 1 сервер, при выполнении не ждет соседа)

Вопросы:
- как использовать единственный экземпляр источника запросов ?
- использует ли эксель какой-то курсор внутри себя для доступа к данным ячеек ? Можно ли использовать несколько потоков для доступа в разные места одного файла экселя ?
- или технология в принципе неудачная ?

# coding: cp1251
from win32com.client import Dispatch
import cx_Oracle
import pythoncom
import fdb
import datetime
import sys
import threading
import pprint
import time
data_n="'01.01.2014'"
data_y="'01.01.2013'"    
res_spaces_queue=[]
ora_mutex=threading.Lock()
def xls(id,target_host,Sheet,x,y,sql):    
    try:
        pythoncom.CoInitialize()
        # подключаемся к целевому серверу с данными и получаем сами данные
        ora_con = cx_Oracle.connect("orauser/orapass@"+target_host+"/XE",threaded=True)
        ora_cur = ora_con.cursor()            
        ora_cur.execute(r"alter session set NLS_DATE_FORMAT = 'DD.MM.YYYY'")        
        ora_cur.execute(sql)
        for result in ora_cur:
            ora_mutex.acquire()            
            # записываем результат в запасник
            res_spaces_queue.append([id,result[0],str(start_time),str(datetime.datetime.now()-start_time)])
            ora_mutex.release()            
            
        ora_cur.close()
        ora_con.close()
    except Exception as e1:
        print(str(id)+" fun error:","Line "+format(sys.exc_info()[-1].tb_lineno),e1.__str__())
    finally:        
        pythoncom.CoUninitialize()
#==================================================================      
def main(argv=None):
    # получаем из посторонней БД данные по параметрам подключения к серверам данных. так получается :)
    fb_conn = fdb.connect(dsn='firebird:d:\\NET_ANALISYS.FDB',user='fbuser',password='fbpass')
    fb_cur = fb_conn.cursor()
  
    SELECT=r"SELECT p.gissz_num,p.lanaddress FROM points p"
    fb_cur.execute(SELECT)
    # открываем сам эксель-файл
    xlApp = Dispatch("Excel.Application")
    xlApp.Visible=True
    xlWb = xlApp.Workbooks.Open(r"d:\documents\sql.xlsx")    
    try:
        # пробегаем по списку серверов из БД
        for row in fb_cur.fetchall():
            # перебираем листы в excel
            for Sheet in xlWb.WorkSheets:
                # перебираем столбцы в листе excel
                for y in range(8,12):
                    # перебираем строки в листе excel
                    for x in range(4,12):
                        # мелкий анализ и исправление данных в целевой ячейке
                        sql=Sheet.Cells(y,x).Formula.upper().strip()
                        sql=sql.replace(":DATA_N",data_n)
                        sql=sql.replace(":DATA_Y",data_y)
                        if sql.find('SELECT ')==0:
                           # перебираем столбцы в листе excel
                            t = threading.Thread(target=xls,name='id'+str(row[0]),args=(row[0],row[1],Sheet.Name,x,y,sql))         
                            t.start()
        
        while threading.activeCount()>1:
            if threading.activeCount()<2:
                print(datetime.datetime.now(),threading.enumerate(),"\n------------",)
            time.sleep(3)
    except Exception as e3:        
        print("main try:","Line "+format(sys.exc_info()[-1].tb_lineno),e3.__str__())
    xlApp.Quit()
#==========================================        
if __name__ == "__main__":
    main()
Возможно, вопрос не к питону, а к экселю.
doza_and
Ace
- или технология в принципе неудачная ?
Скорее да, неудачная. Надо считать все запросы в память. И потом уж запускать кучу потоков. Набивать запросы в Экселе вообще странная идея. По моему мнению лучше в текстовом файле по запросу на строчку набить. Так и генерировать их проще и регулярными выражениями обрабатывать. Но вам конечно виднее. Эксель по моему мнению это GUI к таблицам, если таблиц нет то и эксель в топку.
Ace
ИМХО в этой ситуации чтение в память тоже вариант не очень. Реально 157 целевых серверов, запросов примерно примерно 800, причем текст длинный. Большой расход ОЗУ.

Как можно использовать 1 объект эксель-апп , только глобалвар или можно передать как-то указатель эксель-апп в функцию потока ?
pyuser
По вопросу использования одного COM-объекта в разных потоках смотрите функции:
CoMarshalInterThreadInterfaceInStream
CoGetInterfaceAndReleaseStream
PooH
Ace
ИМХО в этой ситуации чтение в память тоже вариант не очень. Реально 157 целевых серверов, запросов примерно примерно 800, причем текст длинный. Большой расход ОЗУ.
Т.е. чисто текст в память много, а а текст + вся немалая экселевская инфраструктура - нормально?! :)

Опять же, что значит много? Пусть 200 серверов, пусть по 1000 запросов, пусть запросы по килобайту(больше десяти строк по 80 символов, куда же больше?) получаем ~200 мегабайт, у меня одна вкладка Chrom`a жрет больше.

Опять же зачем все сразу в память читать? Организуем пул потоков, штук 20, очередь задач, один поток читает построчно файла(ну пусть даже и экселевский) и ставит задачи в очередь (не все сразу, а так чтобы в очереди не больше, скажем, 30 штук было). Потоки из пула читают задачи из очереди и выполняют. Основной поток следит за наполнением очереди и подкидывает, как весь файл пройден и очередь опустела - закончили.
Ace
2 PooH

Я понимаю, что ситуация изначально ненормальная - странные и разнообразные источники данных, уродливая структура. Увы, это изменить не в силах.

Использование пула потоков кончено имеет место, но эффект конечно будет не тот. КПД вырастет относительно чисто последовательного опроса, но на фоне нормального многопоточного опроса по каждому серверу независимо - не смотрится ВААЩЕ

——————————-

пошел изучать подсказку pyuser

——————————-
Изначально задуманный вариант:
1) создание 1го объекта ексель-апп
2) открытие в нем 1го исходного файла с запросами
3) создать потоки для каждого сервера, которые каждый по-своему со своей скоростью переберет и выполнит запросы на своем целевом сервере.
Тогда не нужен пул и скорость выполнения всей затеи = скорости выполнения всех запросов на самом медленном сервере
PooH
Вдогонку, данные из екселевского файла можно и без екселя считать http://www.python-excel.org/ и не будет проблем с COM в потоках.
Ace
спс, посмотрю технологию без СОМ

фактическая математика такая: 150 узлов, 1132 запроса, суммарный размер строк запросов для одного сервера 1 426 209 байт (измерялось getsizeof()) , итого всего 150*1436209~204 мб. Размер не гигантский, но тоже извращение …

сделаю решение целиком - опубликую
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB