Форум » [x]Harbour » Как сделать многопотоковую проверку Базы ? » Ответить

Как сделать многопотоковую проверку Базы ?

Andrey: Есть ряд задач (переодически возникающих) на которые тратиться много времени. Может быть я и не прав. Вот например: база 20 тыс.записей, каждую запись нужно пройти и проверить заполнение полей, если не заполнено - ошибку в тхт-файл. Все (наверно) делают последовательную обработку, т.е. проверяем базу с 1 по N-запись.. А можно же через потоки сделать ? Подскажите как примерно это можно сделать для хХарбора ? Заранее спасибо..

Ответов - 78, стр: 1 2 3 4 All

alkresin: А в чем, собственно, проблема ? Запускаем поток, открываем в нем базу и проверяем, примерно так: ... hb_threadDetach( hb_threadStart( @TestBase() ) ) ... Function TestBase() use mybase shared new do while !Eof() if ... ... endif skip enddo Return Nil

Andrey: alkresin пишет: А в чем, собственно, проблема ? То что вы мне предлагаете, это ОПЯТЬ ПОСЛЕДОВАТЕЛЬНАЯ проверка базы, только в фоновом потоке. Правильно ? А я хочу МНОГОПОТОЧНУЮ проверку базы... Может я неправильно выражаюсь, тогда извините меня. Я хочу чтобы ну допустим 30-40 потоков проверили базу, причем каждый поток проверял свои записи, а уже проверенные не трогал... Т.е. количество записей делим на количество потоков и каждый поток проверяет СВОИ записи. Вот примерно так хочу ускорить проверку.... Как такое можно реализовать ? Будет ли выигрыш по времени ?

a_sidorov: Обработку записей базы я делаю через dbeval(), работает в десятки раз быстрее. А перебирать по записи, по моему, нерационально.


alkresin: Andrey пишет: Я хочу чтобы ну допустим 30-40 потоков проверили базу, причем каждый поток проверял свои записи, а уже проверенные не трогал... Ну тогда вот так: ... hb_threadDetach( hb_threadStart( @TestBase() ),1,5000 ) hb_threadDetach( hb_threadStart( @TestBase() ),5001,10000 ) hb_threadDetach( hb_threadStart( @TestBase() ),10001,15000 ) hb_threadDetach( hb_threadStart( @TestBase() ),15001,20000 ) ... Function TestBase( nRec1, nRec2 ) use mybase shared new go to nrec1 do while !Eof() .and. Recno()<=nRec2 if ... ... endif skip enddo Return Nil Если будете результаты писать в один текстовый файл, то операцию записи надо mutex'ом синхронизировать ( hb_mutexLock() ... hb_mutexUnLock() ) Будет ли выигрыш по времени ? Может быть :). a_sidorov пишет: Обработку записей базы я делаю через dbeval(), работает в десятки раз быстрее Ну это вы загнули... На локальной БД небольшой выигрыш во времени может иметь место, а в сети разница будет неощутима - временные затраты на операции перемещения по базе и чтения данных существенно выше, чем обработка нескольких строк кода.

Andrey: alkresin пишет: Если будете результаты писать в один текстовый файл, то операцию записи надо mutex'ом синхронизировать ( hb_mutexLock() ... hb_mutexUnLock() ) А можно чуть подробнее.... Допустим: Function Main() ..... cErrorFile := "myerror.log" ..... hb_threadDetach( hb_threadStart( @TestBase() ),1,5000, cErrorFile) hb_threadDetach( hb_threadStart( @TestBase() ),5001,10000, cErrorFile ) hb_threadDetach( hb_threadStart( @TestBase() ),10001,15000, cErrorFile ) hb_threadDetach( hb_threadStart( @TestBase() ),15001,20000, cErrorFile ) ... Function TestBase( nRec1, nRec2, cErrorFile ) LOCAL cError:="" use mybase shared new go to nrec1 do while !Eof() .and. Recno()<=nRec2 if ... ... // если ошибка, то добавляем в строку сError += "Error ...."+CRLF endif skip enddo // --- здесь будет запись в cErrorFile... строки cError ! // Как сделать ? Return Nil

alkresin: Где-то в начале программы: static mutex1 Затем, перед запуском потоков: mutex1 := hb_mutexCreate() Ну и в TestBase(): hb_mutexLock( mutex1 ) // --- здесь будет запись в cErrorFile... строки cError ! hb_mutexUnLock( mutex1 )

Andrey: СПАСИБО БОЛЬШОЕ ! Буду пробовать...

Dima: Andrey пишет: Буду пробовать... Поведай нам потом есть ли выигрыш по скорости. Что то мне кажется если он и будет то будет не существенный.

Andrey: Dima пишет: Поведай нам потом есть ли выигрыш по скорости. Обязательно !

Andrey: alkresin пишет на http://kresin.belgorod.su/rus/hrbfaq.html#Doc11: Другие примеры многопоточных программ вы можете найти в harbour/tests/mt. А для хХарбора где можно найти функции и примеры ? Я пока на хХарборе сижу....

AlexMyr: Andrey пишет: А для хХарбора где можно найти функции и примеры ? в папке tests/mt*.prg

Andrey: Сделал пока однопотоковый тест проверки базы и понял где "тормоза" можно убрать. При проверке базы, если ошибка, то я использую CT-функцию: STRFILE( "Ошибка ... такая то...", cFileError, .T.) Да еще и в цикле проверки. Если её убрать, то проверка просто "влет" ускоряется.... Может ошибки писать в массив, а потом сбрасывать на диск ? Нужно ли заменить STRFILE() и чем ? Народ, подскажите как вы создаете текстовые журналы-ошибок для пользователя ?

PSP: Вот как раз отдельный поток мог бы делать журналирование, имхо.

Andrey: Народ, помогите протестировать мою тестовую программу. А потом пришлите log-файл теста: dBase2test.log У меня комп слишком шустрый для таких тестов... --------------------------------------------------------------------------- CPU: Intel(R) Core(TM) i5-3570K CPU @ 3.40GHz [~3504 MHz] Free RAM: 2 097 151 OS: Windows Windows Vista Professional 6.02.9200 Development: xHarbour build 1.0.0 Intl. (SimpLex) - Borland C++ 5.5.1 Multi Thread: No --------------------------------------------------------------------------- Create/Open Test.dbf - 00 hour 00 minute 01 seconds ( Recno: 30000 ) First test base - 00 hour 00 minute 01 seconds Second test base - 00 hour 00 minute 02 seconds Third test base - 00 hour 00 minute 02 seconds Fourth test base - 00 hour 00 minute 03 seconds The total test time ---> 00 hour 00 minute 11 seconds Это тест на 30000 записей, можно поставить и 70000 (есть такие клиенты). Конечно же реально кол-во ошибок в журналах меньше чем сейчас создается в тесте... Исходник и сама программа здесь - http://files.mail.ru/5A8E598290604D31955459BEFBDF3FD3 Какие будут рекомендации для ускорения работы этой программы для ОДНОПОТОКОВОЙ работы для локальной программы ? Для сетевой задачи журнал-ошибок нужно наверно все таки писать на локальную станцию...

nick_mi: Для сетевой работы журнал лучше писать в dbf файл ИМХО конечно. А если в DBF завести последнее поле длиной 2 символа и туда писать chr(13)+ chr(10) и отформатировать выводимую запись, то такую БД можно будет просматривать любым редактором. За исключением абракадабры в заголовке DBFинфорация будет вполне читаема и без специальных программ типа DBU

Andrey: nick_mi пишет: За исключением абракадабры в заголовке DBFинфорация будет вполне читаема и без специальных программ типа DBU Это пользователь будет читать ? Как ты ему это объяснишь ?

nick_mi: Ну если вопрос стоит о том, чтобы смотрел пользователь, значит для него простенькую программу просмотра, а если уж очень влом писать программу просмотра или журнал вообще предназначен в первую очередь тебе для разборок полетов, в первую запись базы вносишь данные CHR(13) + CHR(10) + "***ЖУРНАЛ РАБОТЫ***" + CHR(13) + CHR(10) . А пользователю объясняешь: Все что до того - служебная информация, а ниже того - для пользовательского внимания

SergKis: Andrey пишет:Какие будут рекомендации Может вот так [pre2] #include "i_winuser.ch" #include "minigui.ch" #include "hbclass.ch" #include "hbwin.ch" MEMVAR oPub FUNCTION MAIN() LOCAL i,j,k, aYes,oL,oE PUBLIC oPub := MyPubl():New() MyFunc() oPub:Destroy() RETURN FUNCTION MyFunc() LOCAL i,j,k, aYes,oL,oE aYes := { "Text ", 10.00, 20.00, Date() } k := 10 oE := oPub:oErr oL := oPub:oLog oE:Put(repl("=",10), Date(), repl("=", 20)) oL:Put(repl("=",10), Date(), repl("=", 20)) FOR i := 1 TO k oPub:oLog:Put( PadR(aYes[1]+ltrim(str(i)), 20), ; PadL(aYes[2] * i, 12), ; PadL(aYes[3] * i, 12), ; PadC(aYes[4] + i, 12) ) IF i == 3 .or. i == 7 oE:Put("Error <", ltrim(str(i)), ">") ENDIF NEXT RETURN CLASS ToFile // Класс вывода в файл txt VAR cFile INIT '' VAR hFile INIT 0 VAR cTab INIT '' VAR cCrLf INIT '' METHOD New() METHOD Open() METHOD Close() METHOD Put() METHOD PutTab() METHOD PutCrLf() METHOD Val2Txt() ENDCLASS METHOD New( cFile ) CLASS ToFile IF ! empty(cFile) .and. valtype(cFile) == "C" ::cFile := cFile ENDIF ::cTab := chr(9) ::cCrLf := chr(13)+chr(10) RETURN Self METHOD Open() IF empty(::cFile); RETURN .F. ENDIF ::hFile := iif( File(::cFile), FOpen(::cFile, 2) , FCreate(::cFile) ) IF ::hFile > 0; FSeek( ::hFile, 0, 2 ) ENDIF RETURN ( ::hFile > 0 ) METHOD Close() CLASS ToFile IF ::hFile > 0 FClose( ::hFile ) ::hFile := 0 ENDIF RETURN METHOD Put( ... ) CLASS ToFile LOCAL i,j, aParams, nParams, lParams nParams := PCount() IF ::hFile < 1 .or. nParams < 1; RETURN ENDIF aParams := hb_aParams() lParams := nParams > 1 FOR i := 1 TO nParams j := ::Val2Txt( aParams [ i ] ) IF len(j) > 0; FWrite(::hFile, j) ENDIF IF i != nParams .and. lParams; ::PutTab() ENDIF NEXT IF lParams; ::PutCrlf() ENDIF RETURN METHOD PutTab() CLASS ToFile IF len(::cTab) > 0; FWrite(::hFile, ::cTab) ENDIF RETURN METHOD PutCrLf() CLASS ToFile IF len(::cCrLf) > 0; FWrite(::hFile, ::cCrLf) ENDIF RETURN METHOD Val2Txt( xVal ) CLASS ToFile LOCAL cTx := "", cTp := valtype(xVal) IF cTp=='C'; cTx := xVal ELSEIF cTp=='N'; cTx := LTrim(Str(xVal)) ELSEIF cTp=='L'; cTx := iif(xVal, "Yes", "No ") ELSEIF cTp=='D'; cTx := DToC( xVal ) ELSEIF cTp=='A'; AEval(xVal, {|x,i| cTx += x + ::cTab }) ENDIF RETURN cTx CLASS MyPubl // Класс контейнер VAR oLog VAR oErr METHOD New() METHOD Destroy() ENDCLASS METHOD New() CLASS MyPubl ::oLog := ToFile():New("_MyLog.txt") ::oErr := ToFile():New("_MyErr.txt") ::oLog:Open() ::oErr:Open() RETURN Self METHOD Destroy() CLASS MyPubl ::oLog:Close() ::oErr:Close() RETURN [/pre2]

Andrey: Да вопрос то по другому стоит: Какие будут рекомендации для ускорения работы этой программы для ОДНОПОТОКОВОЙ работы для локальной программы ? В чем разница по скорости записи: 1) CT-функции STRFILE() 2) Или то что предлагает nick_mi nick_mi пишет: Для сетевой работы журнал лучше писать в dbf файл ИМХО конечно. Запись в DBF это 2 операции: APPEND BLANK и запись в поле STRFILE() наверно 4... (Я не знаток) открыть файл, указатель файла в конец файла, запись в файл, закрыть файл. Самые МЕДЛЕННЫЕ операции - это: открытие и закрытие файлов... Но тогда я сам могу создать функцию записи в журнал. Один раз его открыть в начале, потом писать в него (2 операции, указатель в конец файла и запись строки) и в конце проверки - закрыть файл. Будет наверно быстрее.... Народ, помогите протестировать мою тестовую программу. Ссылка вверху !

nick_mi: An example of measuring the speed of processing database for [x]Harbour Copyright 2013 Verchenko Andrey <verchenkoag@gmail.com> Russia, Dmitrov --------------------------------------------------------------------------- CPU: Intel(R) Pentium(R) CPU G645 @ 2.90GHz [~2894 MHz] Free RAM: 1 298 096 OS: Windows XP Professional 5.01.2600 Service Pack 3 Development: xHarbour build 1.0.0 Intl. (SimpLex) - Borland C++ 5.5.1 Multi Thread: No --------------------------------------------------------------------------- Create/Open Test.dbf - 00 hour 00 minute 03 seconds ( Recno: 30000 ) First test base - 00 hour 00 minute 02 seconds Second test base - 00 hour 00 minute 05 seconds Third test base - 00 hour 00 minute 02 seconds Fourth test base - 00 hour 00 minute 08 seconds The total test time ---> 00 hour 00 minute 21 seconds



полная версия страницы