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

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

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

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

Dima: Andrey пишет: Результат классный !!! The third 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) 4 CPU 3.00GHz [~3011 MHz] Free RAM: 578 452 OS: Windows XP Professional 5.01.2600 Service Pack 2 Development: xHarbour build 1.0.0 Intl. (SimpLex) - Borland C++ 5.5.1 Multi Thread: No --------------------------------------------------------------------------- Create/Open Test.dbf - 00 hour 00 minute 46 seconds ( Recno: 2525/127500 ) Write test base - 00 hour 01 minute 09 seconds The total test time ---> 00 hour 01 minute 56 seconds

Andrey: Dima пишет: CPU: Intel(R) Pentium(R) 4 CPU 3.00GHz [~3011 MHz] Free RAM: 578 452 OS: Windows XP Professional 5.01.2600 Service Pack 2 Значит у моих юзеров тоже будет летать ... Спасибо Дима !

Dima: Andrey пишет: Жалко только опять примера нет на МНОГОПОТОКОВУЮ обработку базы.... ты сам ответил ;) Andrey пишет: Это то что нужно... Главное понимать что и когда делать.. Просто надо сделать оптимальный алгоритм. Она , многопоточность тут скорее всего и не нужна.


Andrey: Dima пишет: Просто надо сделать оптимальный алгоритм. Исправил (убрал DBCOMMIT) у себя в расчетах, вынес в конец обработки. Перерасчет всех абонентов при 70000 абонентов вместо 1ч 20мин. стал занимать 17 минут.... А у других базы еще меньше... Спасибо БОЛЬШОЕ ВСЕМ за помощь !

Andrey: PSP пишет: Не спеши... ))) Я там подправил сообщение. Проверь функцию. update: Проверил в Харборе WIN_OSVersionInfo(). Нормально работает. WIN_OSVersionInfo()[ 2 ] на 7-ке выдало "1". Беру winos.prg из хХарбора 1.2.3 SVN, вставляю в свой проект на 1.2.1, запрашиваю OS_ISWIN8() и получаю .F. (на своей Win8) Как бы им туда сообщить....

Dima: Andrey пишет: Беру winos.prg из хХарбора 1.2.3 SVN, вставляю в свой проект на 1.2.1, запрашиваю OS_ISWIN8() и получаю .F. (на своей Win8) Я бы пересобрал проект под 1.2.3 в этом случае если очень надо. Голый номер брать исходники из новых версий и вставлять в старые версии как правило. Хотя конечно что брать и куда вставлять ...смотреть надо Andrey пишет: Беру winos.prg из хХарбора 1.2.3 SVN, вставляю в свой проект на 1.2.1 Это почти тоже что вставить этот исходник ну скажем в хХарбор 1.0.0 и попытаться получить положительный результат.

PSP: Не знаю, как в хэХарборе, а в просто Харборе эти функции описаны в \contrib\hbwin\win_os.c К примеру, [pre2]HB_FUNC( WIN_OSIS8 ) { OSVERSIONINFO osvi; getwinver( &osvi ); hb_retl( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2 ); } [/pre2]

Andrey: Dima да бесполезно пересобирать... смотри что там в исходнике написано: PSP пишет: Не знаю, как в хэХарборе Там в исходнике winos.prg написано: #pragma BEGINDUMP #if defined(HB_OS_WIN) && (!defined(__RSXNT__)) && (!defined(__CYGWIN__)) ............... HB_FUNC( OS_ISWIN8 ) { OSVERSIONINFO osvi; getwinver( &osvi ); hb_retl( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2 ); } #else ............. HB_FUNC( OS_ISWIN8 ) { hb_retl( 0 ) ; } #endif

Andrey: Вот пример по делу, мультипотоковая индексация БД для Харбора (под хХарбор не делал). Тема примера: возврат значений (переменные cResultError и nResultIndexFile) после выполнения потока. Большое СПАСИБО alkresin ! Без него бы не сделал ! Может кому и пригодиться. //////////////////////////////////////////////////////////////////////////////// // // Index_MT.prg.prg // // Copyright: // Verchenko Andrey <verchenkoag@gmail.com>, (c) 2013. All rights reserved. // An example of Returning values from a stream in the Harbor. // //////////////////////////////////////////////////////////////////////////////// #include "ord.ch" #include "hbthread.ch" #define CLRF Chr( 13 ) + Chr( 10 ) #define PROGRAM " Returning values from a stream in the Harbor" #define COPYRIGHT " Copyright 2013 - http://clipper.borda.ru" #define NUMBER_DATABASE_RECORDS 2500 Static nResultIndexFile, cResultError, nTimerCount FUNCTION MAIN() LOCAL cLogFile := SUBSTR( HB_ArgV(0), 1, RAT(".", HB_ArgV(0) ) ) + "log" LOCAL nTimeStart, nTime2, nTime1, aRec, cStr, cError, aParDbf, threadA REQUEST DBFCDX RddSetDefault( 'DBFCDX' ) SETCURSOR(0) SETMODE(25,80) SETCOLOR("11/1") CLEAR SCREEN MyInfo(cLogFile) nTimeStart := SECONDS() IF !hb_mtvm() ALERT("No support for multi-threading !" + CRLF + CRLF + ; "Collect program with the - /mt !" + CRLF ) QUIT ENDIF // create test base nTime1 := SECONDS() ? ; ? "Create/Open Dbf - " aRec := Create_Test_DBF() ?? MyTime(SECONDS(), nTime1) + "( Recno: "+LTRIM(STR(aRec[1]))+"/"+LTRIM(STR(aRec[2]))+"/"+LTRIM(STR(aRec[3]))+" )" STRFILE("Create/Open Dbf - "+MyTime(SECONDS(), nTime1)+"( Recno: "+LTRIM(STR(aRec[1]))+"/"+LTRIM(STR(aRec[2]))+"/"+LTRIM(STR(aRec[3]))+" )"+CLRF, cLogFile, .T.) ? nTime1 := SECONDS() aParDbf := { {"test2.dbf","ABONENT1","test20.cdx"} , ; {"test1.dbf","DOGOVOR1","test10.cdx"} , ; {"test3.dbf","ABONENT2","test30.cdx"} } cResultError := "" // текст ошибки при индексации nResultIndexFile := 0 // результат индексации nTimerCount := 0 // показ таймера (сек.) индексации // as an example - MassivParametrovBazy, logfiles, Coordinates YX hb_threadDetach( hb_threadStart( @MyIndexFile() , aParDbf, cLogFile, ROW(), COL() ) ) // After creating a stream run timer associated with the procedure DO WHILE !TimerProc() ENDDO STRFILE("Indexing all databases over - "+MyTime(SECONDS(), nTime1)+CLRF, cLogFile, .T.) IF LEN(cResultError) > 0 ALERT("Errors when indexing database !;;"+cResultError ) ENDIF @ MAXROW()-1, 0 SAY " Return @MyIndexFile() -> nResultIndexFile = " + LTRIM(STR(nResultIndexFile)) COLOR("14/1") @ MAXROW() , 0 SAY PADC(" End work ",MAXCOL()+1,"-") COLOR("15/4") INKEY(0) CLOSE ALL RETURN NIL //////////////////////////////////////////////////////////////////////////////// Function TimerProc Local nSec := Seconds() @ MAXROW(), 5 SAY Str(nTimerCount++) COLOR("15/4") INKEY(1) IF nResultIndexFile != 0 // Hence, the thread has terminated, to inform about the result of his work // Close timer Return .T. ENDIF Return .F. //////////////////////////////////////////////////////////////////////////////// FUNCTION MyIndexFile(aParDbf, cLogFile, nY, nX ) LOCAL nI, nJ, nG, cStr, nLastRecno, nY2, nX2 LOCAL cIndexTo, cFilterTo, cFileIndex, nKolRecords LOCAL bErrHandler, cDbfName, cDbfAlias, nErrDos cStr := CLRF + CLRF + PADC("",75,"-") + CLRF cStr += PADC("Indexing databases in a separate thread",75) + CLRF cStr += PADC("",75,"-") + CLRF + CLRF STRFILE(cStr, cLogFile, .T.) FOR nI := 1 TO LEN(aParDbf) cDbfName := aParDbf[nI,1] cDbfAlias := aParDbf[nI,2] cFileIndex := aParDbf[nI,3] @ nY + nI*2, nX SAY " Use Dbase - " + cDbfName bErrHandler := ErrorBlock( { | VAR | BREAK( VAR ) } ) BEGIN SEQUENCE USE ( cDbfName ) Alias ( cDbfAlias ) EXCLUSIVE NEW RECOVER cStr := " ->-> ERROR! DB-" + cDbfName + " busy with another task !" @ nY + nI*2, nX+26 SAY cStr STRFILE(cStr, cLogFile, .T.) nResultIndexFile := -2 // Unable to open the database cResultError += cStr + ";" RETURN NIL END SEQUENCE ErrorBlock( bErrHandler ) nKolRecords := LASTREC() cIndexTo := "NN" cFilterTo := "!DELETED()" IF FILE (cFileIndex) nErrDos := DELETEFILE( cFileIndex ) IF nErrDos # 0 cStr := " ->-> ERROR ("+LTrim( Str( nErrDos ) )+") ! Access denied to delete a file " + cFileIndex + " !" @ nY + nI*2, nX+26 SAY cStr STRFILE(cStr, cLogFile, .T.) nResultIndexFile := -3 // Unable to delete the index file cResultError += cStr + ";" RETURN NIL ENDIF ENDIF @ nY + nI*2, nX+26 SAY "Index base - " + cFileIndex + " -> " ; nY2 := ROW() ; nX2 := COL() INDEX ON &cIndexTo TO (cFileIndex) ; EVAL SAY_PROC( nY2, nX2, RECNO(), nKolRecords) ; EVERY nKolRecords / 10 FOR &cFilterTo NEXT nResultIndexFile := 1 // No error - on the basis of the current RETURN NIL //////////////////////////////////////////////////////////////////////////////// FUNCTION Create_Test_DBF() LOCAL aDbf1, aDbf2, nI, nJ, nK, n1, n2, cStr, nNN, nID := 100 LOCAL cTable2 := "test2.dbf", cTable1 := "test1.dbf", cTable3 := "test3.dbf" LOCAL cPathFolder := hb_CurDrive() + ":\" + CurDir() + '\' LOCAL nY := ROW(), nX := COL() aDbf2 := { { "FIO" , "C", 35, 0 }, ; { "LastName" , "C", 20, 0 }, ; { "YES" , "N", 2, 0 }, ; { "NN" , "N", 8, 0 }, ; { "NNN" , "N", 8, 0 }, ; { "KTARIF" , "N", 2, 0 }, ; { "SKIDKA" , "N", 3, 0 }, ; { "NDOG" , "C", 12, 0 }, ; { "KOkrug" , "N", 6, 0 }, ; { "KDez" , "N", 6, 0 }, ; { "KCity" , "N", 6, 0 }, ; { "KStreet" , "N", 6, 0 }, ; { "cNumHOUSE", "C", 10, 0 }, ; { "NumKorp" , "N", 4, 0 }, ; { "NumPodezd", "N", 2, 0 }, ; { "cNumKvar" , "C", 10, 0 }, ; { "KStDogov" , "N", 2, 0 }, ; { "KVid_to" , "N", 2, 0 }, ; { "KVidOpl" , "N", 2, 0 }, ; { "KPVidOpl" , "N", 2, 0 }, ; { "KFili" , "N", 2, 0 }, ; { "KFiliDop" , "N", 2, 0 }, ; { "KFiliDsp" , "N", 2, 0 }, ; { "DateRas1" , "D", 8, 0 }, ; { "KUCompany", "N", 2, 0 }, ; { "KEIRC" , "N", 5, 0 }, ; { "KTipZak" , "N", 3, 0 }, ; { "KZakaz" , "N", 3, 0 }, ; { "RC_abon" , "C", 8, 0 }, ; { "RC_abon0" , "C", 8, 0 }, ; { "RC_abon3" , "C", 8, 0 }, ; { "RC_abon4" , "C", 8, 0 }, ; { "RC_NEW18" , "C", 18, 0 }, ; { "Logik" , "L", 1, 0 } } // aadd field memo AADD( aDbf2, { "MEMOTEST" , "M", 10, 0 } ) aDbf1 := { { "FIOST" , "C", 35, 0 }, ; { "LastNam2" , "C", 20, 0 }, ; { "Age" , "N", 3, 0 }, ; { "Date" , "D", 8, 0 }, ; { "Rate" , "N", 6, 2 }, ; { "DateRas2" , "D", 8, 0 }, ; { "DateDog" , "D", 8, 0 }, ; { "NN" , "N", 8, 0 }, ; { "NDOG" , "C", 12, 0 }, ; { "KOkrug" , "N", 6, 0 }, ; { "KDez" , "N", 6, 0 }, ; { "KCity" , "N", 6, 0 }, ; { "KStreet" , "N", 6, 0 }, ; { "cNumHOUSE", "C", 10, 0 }, ; { "NumKorp" , "N", 4, 0 }, ; { "NumPodezd", "N", 2, 0 }, ; { "cNumKvar" , "C", 10, 0 }, ; { "KStDogov" , "N", 2, 0 }, ; { "KVid_to" , "N", 2, 0 }, ; { "KVidOpl" , "N", 2, 0 }, ; { "KPVidOpl" , "N", 2, 0 }, ; { "KFili" , "N", 2, 0 }, ; { "KFiliDop" , "N", 2, 0 }, ; { "KFiliDsp" , "N", 2, 0 }, ; { "DateRas1" , "D", 8, 0 }, ; { "KUCompany", "N", 2, 0 }, ; { "KEIRC" , "N", 5, 0 }, ; { "KTipZak" , "N", 3, 0 }, ; { "KZakaz" , "N", 3, 0 }, ; { "MEMO2" , "M", 10, 0 } } IF !FILE(cTable1) // if there is no database, then create it DBCreate( cTable1, aDbf1 ) USE ( cTable1 ) ALIAS DOGOVOR NEW DBCreate( cTable2, aDbf2 ) USE ( cTable2 ) ALIAS ABONENT NEW SELECT DOGOVOR FOR nI := 1 TO NUMBER_DATABASE_RECORDS @ nY, nX + 3 SAY "Recno:"+LTRIM(STR(nI)) SELECT DOGOVOR APPEND BLANK n1 := hb_RandomInt( 80 ) n2 := hb_RandomInt( 50 ) REPLACE FIELD->Age WITH n1, ; FIELD->Date WITH Date() - 365 * n2 + n1, ; FIELD->Rate WITH 56.5 - n1 / 2 REPLACE FIELD->FIOST WITH "Dog" + Chr( 64 + n2 ) + PadL( nI, 10, '0' ), ; FIELD->LastNam2 WITH "MyB" + Chr( 70 + n2 ) + PadL( nI, 12, '0' ) FIELD->NN := nI FIELD->NDOG := "D"+LTRIM(STR(nI))+"/"+LTRIM(STR(n2)) FIELD->KOkrug := n1 FIELD->KDez := n2 FIELD->KCity := n1-8 FIELD->KStreet := n2-9 FIELD->cNumHOUSE:= LTRIM(STR(n1-n2))+Chr( 60 + n2 ) FIELD->NumKorp := IIF( nI % 30 == 0 , 1 , 0 ) FIELD->NumPodezd:= 0 FIELD->KStDogov := 4 FIELD->KVid_to := 1 FIELD->KVidOpl := 2 FIELD->KPVidOpl := 3 FIELD->KFili := 1 FIELD->KFiliDop := 1 FIELD->KFiliDsp := 2 FIELD->DateRas1 := Date() - 365 * n2 + n1 FIELD->KUCompany:= 10 FIELD->KEIRC := 11 FIELD->KTipZak := 12 FIELD->KZakaz := 13 FOR nJ := 1 TO 51 // number of subscribers nID := nID + nJ + 1 SELECT ABONENT APPEND BLANK FIELD->NN := nI FIELD->cNumKvar := LTRIM(STR(nJ)) FIELD->NNN := nID++ FIELD->KTARIF := IIF( nI % 800 == 0 , 0 , n1/10 ) FIELD->SKIDKA := IIF( nI % 300 == 0 , 100 , 0 ) cStr := PADL(ALLTRIM(STR(RECNO())),8,"0") FIELD->RC_abon:= IIF( nI % 150 == 0 , "", cStr ) IF nI % 500 == 0 cStr := PADL(ALLTRIM(STR(RECNO()-11)),8,"0") FIELD->RC_abon:= cStr ENDIF FIELD->FIO := "Abon" + Chr( 64 + n2 ) + PadL( nI, 20, '0' ) FIELD->MEMOTEST := "test: " + ALLTRIM( STR(nI) ) IF nJ == 49 // emulation error FIELD->NN := 0 ENDIF IF nJ == 50 // emulation error FIELD->NN := nI+2000 ENDIF IF nJ == 51 // emulation recno deleted DBDELETE() ENDIF NEXT // emulation recno deleted IF nI % 100 == 0 SELECT DOGOVOR APPEND BLANK DELETE ENDIF NEXT SELECT DOGOVOR nI := LASTREC() CLOSE DOGOVOR SELECT ABONENT nJ := LASTREC() CLOSE ABONENT FILECOPY (cPathFolder + cTable2, cPathFolder + cTable3) FILECOPY (cPathFolder + "test2.fpt", cPathFolder + "test3.fpt") nK := nJ ELSE USE ( cTable1 ) NEW nI := LASTREC() USE USE ( cTable2 ) NEW nJ := LASTREC() USE USE ( cTable3 ) NEW nK := LASTREC() USE ENDIF RETURN {nI,nJ,nK} //////////////////////////////////////////////////////////////////////////////// Function MyInfo(cLogFile) LOCAL aStr := {}, nI AADD( aStr , PROGRAM ) AADD( aStr , COPYRIGHT ) AADD( aStr , PADC("",75,"-") ) AADD( aStr , " Free RAM: " + ALLTRIM(TRANSFORM(memory(0),"999 999 999")) ) AADD( aStr , " OS: " + OS() ) AADD( aStr , " Development: " + Version() + " - " + hb_compiler() ) AADD( aStr , " Multi Thread: " + IIF( hb_mtvm()==.Y., "Yes", "No" ) ) AADD( aStr , PADC("",75,"-") ) STRFILE("", cLogFile, .F.) FOR nI := 1 TO LEN(aStr) ? aStr[nI] STRFILE(aStr[nI]+CLRF, cLogFile, .T.) NEXT RETURN NIL //////////////////////////////////////////////////////////////////////////////// // Returns: a string of the past tense FUNCTION MyTIME(nTime2, nTime1) LOCAL cTime, cRet cTime := SECTOTIME(nTime2 - nTime1) cRet := SUBSTR(cTime,4,2)+" minute " + ; SUBSTR(cTime,7,2)+" seconds " RETURN cRet //////////////////////////////////////////////////////////////////////////////// FUNCTION SAY_PROC( y, x, nRecno, nKolRecords ) DevPos( y, x+3 ) DevOutPict( Int( nRecno / nKolRecords*100 ), "999%" ) INKEY(0.5) RETURN .T.

nick_mi: Спасибо за пример Андрей! Но в программе вкралась ошибка. По Невнимательности собрал программу без -mt и программа вылетела с ошибкой нет переменной CRLF. У тебя в программе определена #define CLRF, и при правильной работе именно она используется, а при неправильной сборке задействована CRLF для формирования сообщения.

Andrey: nick_mi пишет: У тебя в программе определена #define CLRF, и при правильной работе именно она используется, Ну уж не досмотрел....

nick_mi: Бывает!

Andrey: Возвращаюсь опять к потокам. Где считается база быстрей (или одинаково) в потоке или в основной программе ? Т.е. создаю отдельный поток для расчёта по базе, а основную программу оставляю в ожидание окончания потока. По времени как будет ? Одинаково или нет ? И еще вопрос: можно ли в отдельном потоке создавать окна (МиниГуи), а после выполнения потока просто оставить не убитым. Что будет ?

Alex_Cher: Andrey пишет: Исходник и сама программа здесь - http://files.mail.ru/5A8E598290604D31955459BEFBDF3FD3 Ошибка 404 ... не могу скачать

Andrey: Alex_Cher пишет: .. не могу скачать А какую программу скачиваете ? Я может убил её давно...

Dima: Andrey пишет: Я может убил её давно... Так объем облака 100 Гб ....экономишь ?

Andrey: Dima пишет: Так объем облака 100 Гб ....экономишь ? Старые примеры иногда переделываю...

Alex_Cher: Dima пишет: Так объем облака 100 Гб ....экономишь ? Нет мужики это я лохонулся ... тема интересная а сроки выкладки сообщения не посмотрел ...



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