Автор | Сообщение |
|
| постоянный участник
|
Пост N: 2517
Зарегистрирован: 12.09.06
|
|
Отправлено: 26.12.12 14:01. Заголовок: Как сделать многопотоковую проверку Базы ?
Есть ряд задач (переодически возникающих) на которые тратиться много времени. Может быть я и не прав. Вот например: база 20 тыс.записей, каждую запись нужно пройти и проверить заполнение полей, если не заполнено - ошибку в тхт-файл. Все (наверно) делают последовательную обработку, т.е. проверяем базу с 1 по N-запись.. А можно же через потоки сделать ? Подскажите как примерно это можно сделать для хХарбора ? Заранее спасибо..
| |
|
Ответов - 78
, стр:
1
2
3
4
All
[только новые]
|
|
|
| |
Пост N: 2627
Зарегистрирован: 17.05.05
|
|
Отправлено: 15.02.13 18:53. Заголовок: Andrey пишет: Резул..
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
| |
|
|
| постоянный участник
|
Пост N: 2671
Зарегистрирован: 12.09.06
|
|
Отправлено: 15.02.13 19:32. Заголовок: Dima пишет: CPU: In..
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 |
| Значит у моих юзеров тоже будет летать ... Спасибо Дима !
| |
|
|
| |
Пост N: 2628
Зарегистрирован: 17.05.05
|
|
Отправлено: 15.02.13 22:22. Заголовок: Andrey пишет: Жалко..
Andrey пишет: цитата: | Жалко только опять примера нет на МНОГОПОТОКОВУЮ обработку базы.... |
| ты сам ответил ;) Andrey пишет: цитата: | Это то что нужно... Главное понимать что и когда делать.. |
| Просто надо сделать оптимальный алгоритм. Она , многопоточность тут скорее всего и не нужна.
| |
|
|
| постоянный участник
|
Пост N: 2692
Зарегистрирован: 12.09.06
|
|
Отправлено: 20.02.13 12:38. Заголовок: Dima пишет: Просто ..
Dima пишет: цитата: | Просто надо сделать оптимальный алгоритм. |
| Исправил (убрал DBCOMMIT) у себя в расчетах, вынес в конец обработки. Перерасчет всех абонентов при 70000 абонентов вместо 1ч 20мин. стал занимать 17 минут.... А у других базы еще меньше... Спасибо БОЛЬШОЕ ВСЕМ за помощь !
| |
|
|
| постоянный участник
|
Пост N: 2726
Зарегистрирован: 12.09.06
|
|
Отправлено: 04.03.13 23:19. Заголовок: PSP пишет: Не спеши..
PSP пишет: цитата: | Не спеши... ))) Я там подправил сообщение. Проверь функцию. update: Проверил в Харборе WIN_OSVersionInfo(). Нормально работает. WIN_OSVersionInfo()[ 2 ] на 7-ке выдало "1". |
| Беру winos.prg из хХарбора 1.2.3 SVN, вставляю в свой проект на 1.2.1, запрашиваю OS_ISWIN8() и получаю .F. (на своей Win8) Как бы им туда сообщить....
| |
|
|
| |
Пост N: 2672
Зарегистрирован: 17.05.05
|
|
Отправлено: 05.03.13 00:02. Заголовок: Andrey пишет: Беру ..
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 и попытаться получить положительный результат.
| |
|
|
| постоянный участник
|
Пост N: 842
Зарегистрирован: 27.01.07
|
|
Отправлено: 05.03.13 08:47. Заголовок: Не знаю, как в хэХар..
Не знаю, как в хэХарборе, а в просто Харборе эти функции описаны в \contrib\hbwin\win_os.c К примеру, HB_FUNC( WIN_OSIS8 ) { OSVERSIONINFO osvi; getwinver( &osvi ); hb_retl( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2 ); }
| |
|
|
| постоянный участник
|
Пост N: 2727
Зарегистрирован: 12.09.06
|
|
Отправлено: 05.03.13 10:50. Заголовок: Dima да бесполезно п..
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
| |
|
|
| постоянный участник
|
Пост N: 3228
Зарегистрирован: 12.09.06
|
|
Отправлено: 04.02.14 18:58. Заголовок: Вот пример по делу, ..
Вот пример по делу, мультипотоковая индексация БД для Харбора (под хХарбор не делал). Тема примера: возврат значений (переменные 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.
|
| |
|
|
| |
Пост N: 139
Зарегистрирован: 19.05.05
|
|
Отправлено: 06.02.14 09:21. Заголовок: Спасибо за пример Ан..
Спасибо за пример Андрей! Но в программе вкралась ошибка. По Невнимательности собрал программу без -mt и программа вылетела с ошибкой нет переменной CRLF. У тебя в программе определена #define CLRF, и при правильной работе именно она используется, а при неправильной сборке задействована CRLF для формирования сообщения.
| |
|
|
| постоянный участник
|
Пост N: 3229
Зарегистрирован: 12.09.06
|
|
Отправлено: 06.02.14 16:33. Заголовок: nick_mi пишет: У те..
nick_mi пишет: цитата: | У тебя в программе определена #define CLRF, и при правильной работе именно она используется, |
| Ну уж не досмотрел....
| |
|
|
|
| |
Пост N: 140
Зарегистрирован: 19.05.05
|
|
Отправлено: 06.02.14 19:34. Заголовок: Бывает! ..
Бывает!
| |
|
|
| постоянный участник
|
Пост N: 4324
Зарегистрирован: 12.09.06
|
|
Отправлено: 30.06.15 14:28. Заголовок: Возвращаюсь опять к ..
Возвращаюсь опять к потокам. Где считается база быстрей (или одинаково) в потоке или в основной программе ? Т.е. создаю отдельный поток для расчёта по базе, а основную программу оставляю в ожидание окончания потока. По времени как будет ? Одинаково или нет ? И еще вопрос: можно ли в отдельном потоке создавать окна (МиниГуи), а после выполнения потока просто оставить не убитым. Что будет ?
| |
|
|
| |
Пост N: 5
Зарегистрирован: 18.06.15
|
|
Отправлено: 01.07.15 08:28. Заголовок: Andrey пишет: Исхо..
Andrey пишет: Ошибка 404
| |
|
|
| постоянный участник
|
Пост N: 4335
Зарегистрирован: 12.09.06
|
|
Отправлено: 01.07.15 09:16. Заголовок: Alex_Cher пишет: ....
Alex_Cher пишет: А какую программу скачиваете ? Я может убил её давно...
| |
|
|
| |
Пост N: 5006
Зарегистрирован: 17.05.05
|
|
Отправлено: 01.07.15 13:35. Заголовок: Andrey пишет: Я мож..
Andrey пишет: Так объем облака 100 Гб ....экономишь ?
| |
|
|
| постоянный участник
|
Пост N: 4338
Зарегистрирован: 12.09.06
|
|
Отправлено: 01.07.15 13:53. Заголовок: Dima пишет: Так объ..
Dima пишет: цитата: | Так объем облака 100 Гб ....экономишь ? |
| Старые примеры иногда переделываю...
| |
|
|
| |
Пост N: 6
Зарегистрирован: 18.06.15
|
|
Отправлено: 02.07.15 07:54. Заголовок: Dima пишет: Так объ..
Dima пишет: цитата: | Так объем облака 100 Гб ....экономишь ? |
| Нет мужики это я лохонулся ... тема интересная а сроки выкладки сообщения не посмотрел ...
| |
|
Ответов - 78
, стр:
1
2
3
4
All
[только новые]
|
|
|