Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Потоки [D7, WinXP]


Страницы:   1 2 3

YURY ©   (2011-11-29 11:42:00) 

Добрый день.
Требуется выполнить n потоков одного класса, каждый из которых будет выполнять свои действия в зависимости от заданных параметров. Поток Б должен стартовать после завершения потока А.
Реализую на простейшем примере из двух потоков (форма с двумя компонентами TEdit и кнопкой и модуль класса потока). При нажатии на кнопку первый поток должен выводить в первый TEdit значения от 0 до 20000, затем второй поток должен выводить во второй TEdit значения от 50000 до 70000. По факту же после завершения работы первого потока возникает ошибка "Thread error: Неверный дескриптор (6)" и второй поток не отрабатывает. Почему возникает ошибка и как её устранить?
Код для примера (форма и модуль):

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls;

type
 TForm1 = class(TForm)
   Button1: TButton;
   Edit1: TEdit;
   Edit2: TEdit;
   procedure Button1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1: TForm1;

implementation

uses Unit2;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
 ThreadArray: Array of TMyThread;
begin
 SetLength(ThreadArray, 2);

 ThreadArray[0] := TMyThread.Create(True);
 ThreadArray[0].FreeOnTerminate := True;
 ThreadArray[0].Priority := tpNormal;
 ThreadArray[0].j := 0;
 ThreadArray[0].k := 20000;
 ThreadArray[0].ptrEdit := @Edit1;

 ThreadArray[1] := TMyThread.Create(True);
 ThreadArray[1].FreeOnTerminate := True;
 ThreadArray[1].Priority := tpNormal;
 ThreadArray[1].j := 50000;
 ThreadArray[1].k := 70000;
 ThreadArray[1].ptrEdit := @Edit2;
 ThreadArray[1].ptrThread := @ThreadArray[0];

 ThreadArray[0].Resume;
 ThreadArray[1].Resume;
end;

end.

unit Unit2;

interface

uses
 Classes, StdCtrls, SysUtils;

type
 TMyThread = class(TThread)
 public
   j, k: Integer;
   ptrEdit: ^TEdit;
   ptrThread: ^TMyThread;
 protected
   procedure Execute; override;
 end;

implementation

procedure TMyThread.Execute;
var
 i: Integer;
begin
 if Assigned(ptrThread) then ptrThread^.WaitFor;
 for i := j to k do
   ptrEdit^.Text := IntToStr(i);
end;

end.

P.S. Если в модуле потока убрать инструкцию "if Assigned(ptrThread) then ptrThread^.WaitFor;", то оба потока отрабатывают синхронно, но вся суть в том, чтобы они работали последовательно.


Медвежонок Пятачок   (2011-11-29 13:20:00) [1]

да все намного банальнее.
вместо того, чтобы подумать над тем, что же я должен напрограммировать,

Но в том и суть, что не все они должны запускаться параллельно, а чать из них - строго после окончания других.


он начинает сразу проектировать некие абстрактные потоки, которые никак не отвечают поставленной задаче.
Оказывается эти потоки нужно диспетчеризировать.
А диспетчера нет.
Поэтому костыль в виде вайтфор и прочего.

В то время как решение [52] - на поверхности лежит и руками машет. Медвежонок Пятачок © Медвежонок Пятачок, проблема в том, что заранее я не знаю сколько хранимок,

1. Заранее знать не надо.
Надо обработать выбор юзера, что он там себе навыбирал.
Затем осмыслить его и формализовать. Медвежонок Пятачок © Бил гейтц, когда писал свой инет проводник, тоже не знал, пойдешь ли ты на мастера делфи его программой.
Однако же написал его и он даже ходит на мастера делфи. YURY Anatoly Podgoretsky, тощущение, что мы друг друга не понимаем ) Давайте так. Хранимка А выполняется за 15 минут. Хранимка Б за 40. При последовательном запуске обоих хранимок общее время выполнения 55 минут. При параллельном (используя потоки) - 40 минут 5 секунд. Имеем выигрыш во времени 14 минут 55 секунд. Это реальные данные. Вообще же хранимок несколько десятков и их количество увеличивается со временем. Медвежонок Пятачок © Поэтому я не могу заранее создать несколько разных классов потоков - каждый для своей хранимки.

Нужен ОДИН класс потока.

Который умеет запускать одну хранимку, или список хранимок последовательно.

PS Чернилами для седьмого класса можно писать и в первом и в седьмом и в десятом классах.
И даже на пенсии можно писать чернилами для седьмого класса. sniknik © > При параллельном (используя потоки) - 40 минут 5 секунд.
т.е. вопрос решен? потоки работают.

или
> Имеем выигрыш во времени 14 минут 55 секунд.
делим шкуру не убитого медведя?

вообще ИМХО, вот это
> что не все они должны запускаться параллельно, а чать из них - строго после окончания других.
наводит на мысль что потоки вообще не нужны...
нужен ассинхронный режим, с, у некоторых, в "онкомплит" запуском еще одного выполнения. YURY Медвежонок Пятачок, в реальности поток один и он простейший.

type
 TMyThread = class(TThread)
 private
   { Private declarations }
 public
   _ADOQuery     : TADOQuery;
   _Connection   : TADOConnection;
   _ProcedureName: String;
   _Tag          : Longint;
   Msg           : String;
   ReturnValue   : Integer;
 protected
   procedure Execute; override;
 end;

implementation

procedure TMyThread.Execute;
var
 i: Integer;
begin
 with TADOStoredProc.Create(nil) do
 try
   CommandTimeout := 0;
   CursorLocation := clUseServer;
   Connection     := _Connection;
   ProcedureName  := _ProcedureName;
   Parameters.Refresh;
   Parameters.ParamValues['@Msg'] := '';

   for i := 0 to Parameters.Count - 1 do
     if (Parameters.Items[i].Direction = pdInput) and
        (_ADOQuery.Locate('f_ProcName;f_ParamName',
               VarArrayOf([ProcedureName, Parameters.Items[i].Name]), [])) and
        (not _ADOQuery.FieldByName('f_Value').IsNull) then
       Parameters.Items[i].Value := _ADOQuery.FieldValues['f_Value'];

   ExecProc;

   Msg := Parameters.ParamValues['@Msg'];
   ReturnValue := Parameters.ParamValues['@Return_Value'];
 finally
   Free;
 end;
end;

Осталось только научить его ожидать завершения другого потока этого же класса, если таковое действие необходимо (узнает он об этом через какой-нибудь признак во входных параметрах). Но вот научить не получается - ошибка та же, что и в первоначально приведённом примере с VCL... Медвежонок Пятачок © Осталось только научить его ожидать завершения другого потока этого же класса,

Не, это клиника.
Лучше научи своего кота программировать.
У него быстрее получится. sniknik © > ошибка та же, что и в первоначально приведённом примере с VCL...
ну так и читай ветку сначала...
> а не обращайся к объектам VCL формы без синхронизации. или думаешь обманул систему своими указателями...?
и т.д. ничего же не изменилось. Сергей М. ©
> Осталось только научить его ожидать завершения другого потока


Ты чего какой настырный ?)

Зачем ты упорно пытаешься заставить рабочий поток класса TMyThread выполнять совершенно несвойственную ему работу ? Да ему не должно быть никакого дела до других потоков, за исключением основного, если он полезет еще и в VCL ! Ему сказано - вызови к выполнению таку-то ХП - вот пусть он только этим и занимается .. А абитражом должен заниматься совсем другой поток, тот самый который заведует общим списком заданий.

with TADOStoredProc.Create(nil) do
try
  CommandTimeout := 0;
  CursorLocation := clUseServer;
  Connection     := _Connection; // ждут тебя здесь грабли, ибо коннекшн создан у тебя совсем в другом потоке Медвежонок Пятачок © TMyThread = class(TThread)
private
  { Private declarations }
 _ADOQuery     : TADOCommand;
 _Connection   : TADOConnection;
public
  _ProcNames: TStrings; YURY Я потому и обратился сюда, ибо, видимо, не умею программировать. Умел бы - сделал бы без вопросов. Но никто так и не указал где конкретно ошибка и почему она происходит. Не поняв этого, я еще долго буду биться в пустых Сизифовых попытках. Но в любом случае спасибо тем, кто указывает на мои глупости и неумения. Было бы хуже, если бы попросту проигнорировали. DiamondShark ©
> Но никто так и не указал где конкретно ошибка и почему она происходит.

Ты троль. Медвежонок Пятачок © // ждут тебя здесь грабли, ибо коннекшн создан у тебя совсем в другом потоке

Да не, это у него так, - для примера, для простоты.
А на самом деле реальный код совсем другой и он идеален и совершенен. YURY Сергей М., граблей не наблюдается. Я привёл реально работающий поток, который прекрасно показывает себя при параллельной работе нескольких десятков хранимок в бою. sniknik © > А абитражом должен заниматься совсем другой поток, тот самый который заведует общим списком заданий.
нафига? если уж выбирать такой путь (тупой если честно), то ничего не нужно дополнительно, просто в поток нужно передавать не 1 значение на выполнение, а список, и пусть "крутится" пока список не кончился. одно там выполнение, или два, или десяток подряд неважно, все решается анализом (из БД?) и подготовкой перед "на выполнение". Медвежонок Пятачок © Я привёл реально работающий поток, который прекрасно показывает себя при параллельной работе нескольких десятков хранимок в бою.

Все!
Остановись и ничего не трогай!
Пусть так и работает. Сергей М. ©
> никто так и не указал где конкретно ошибка и почему она
> происходит


Она происходит потому что :

if Assigned(ptrThread) then // это условие всегда истинно
// в этот момент другой поток потенциально завершает свое выполнение и убивает сам себя, ибо FreeOnTerminate = True
 ptrThread^.WaitFor; // а здесь ты получаешь граблями, потому как обращаешься к трупу Anatoly Podgoretsky © > YURY  (29.11.2011 13:18:56)  [56]

Какие выберет не знаешь, но откуда то знаешь какие при этом параметры
указывать. Странно это. Anatoly Podgoretsky ©
> YURY   (29.11.11 13:47) [68]

Сними очки или позови кота, мы ему все еще раз объясним. Anatoly Podgoretsky ©
> Медвежонок Пятачок ©   (29.11.11 13:48) [70]

Кривые примеры нам не нужны Anatoly Podgoretsky © Чувствую, что скоро придется тему закрывать из-за бесперктивности или посылать в Потрепаться. YURY_ Anatoly Podgoretsky, список параметров хранимок я получаю сам, а значения параметров пользователь задаёт через интерфейс. Эти значения я считываю запросом. В только что приведённом рабочем потоке видно, как эти значения отыскиваются в зависимости от имени хранимки и заполняют её параметры. YURY_ Сергей М., почему к трупу? Ведь в примере с VCL второй поток не стартует совместно с первым, а именно ожидает несколько секунд, пока первый не отработает. Т.е. Ожидание завершения происходит, но по какой-то причине не происходит возобновление работы второго потока. Кщд YURY   (29.11.11 13:50) [71]
dbms_scheduler решает Вашу задачу Сергей М. ©
> sniknik ©   (29.11.11 13:50) [72]
>  то ничего не нужно дополнительно, просто в поток нужно
> передавать не 1 значение на выполнение, а список


А список-то это кто будет формировать, Пушкин ?)
Его поток-арбитр и должен сформировать)
И с элементами этого списка так или иначе должны быть ассоциированы предварительно созлданные объекты синхронизации, которыми поток, получивший список, будет руководствоваться при принятии решения где ему подождать, а где он может сразу приступить к выполнению элемента Медвежонок Пятачок © но по какой-то причине не происходит возобновление работы второго потока.

а его кто-то приостанавливал?
им обоим resume сделали. YURY_ Медвежонок Пятачок, WaitFor и приостановил. Сергей М. ©
> почему к трупу?


Потому что 1-й поток, которого 2-й будет ожидать, к моменту вызова 2-м потоком метода WaitFor запросто может успеть убить себя, закрыв при этом свой хэндл.  Что собссно и происходит, потому как сообщение "Thread error: Неверный дескриптор (6)" именно об этом тебе и сигнализирует. Медвежонок Пятачок © Приостанавливает суспенд. Вайтфор не приостанавливает ни разу. Сергей М. ©
> WaitFor и приостановил


Не успел)
Тот кого ты собрался ждать по  WaitFor, к этому моменту уже отбыл в мир иной, так что никакой приостановки не было) YURY_ Сергей М., в данном случае я на форме наюлюдаю несколько секунд, как меняются значения в Edit1 от 0 до 20000 и только после этого происходит исключение. Если же WaitFor убрать, то оба потока стартуют одновременно и те же несколько секунд я наблюдаю изменение значений в обоих TEdit. Медвежонок Пятачок © закройте уже тему, а? YURY_ Медвежонок Пятачок, а вот тут сказано, что приостанавливает: "Попросту говоря, когда поток А вызывает метод WaitFor потока B, сам он приостановливается, пока поток B не завершится. "
http://forum.vingrad.ru/forum/topic-60076.html#st_0_view_0 Медвежонок Пятачок © Ну так я тебе могу так же сказать, что любая вообще функция приостанавливает поток точно таким же образом.
На все время, пока не произойдет ретурн из нее.

Что дальше? YURY_ Закрыть тему, конечно, проще всего, но хотелось разобраться почему происходит ошибка. Медвежонок Пятачок © закройте тему, а?

/* украина, политика, путин, газ, власти скрывают, нло, киркоров родил дочь от суррогатных матери и отца..... */ sniknik © > почему происходит ошибка.
потому что ты дебил, иного объяснения при куче ответов/объяснений на эту тему не вижу. YURY_ Медвежонок Пятачок, если такая функция выполняется внутри потока, то она не приостанавливает поток - он выполняется в виде этой самой функции. Разве не так? Сергей М. ©
> и только после этого происходит исключение


Так я ж тебе дважды нарисовал картину маслом почему оно происходит)
Что опять не понятно-то ? sniknik © > Приостанавливает суспенд.
с другой стороны и суспенд работает не так как ожидается у него в коде, т.е. прямая замена (если кто то, не будем показывать пальцем, про это подумал) не поможет. YURY_ sniknik, ну зачем на личности переходить? Лучше бы аргументировали. Ваше предположение о том, что первый поток завершился к моменту вызова WaitFor во втором потоке я опровергнул. Можете сами проверить - 1 минута времени требуется. Значит, дело в другом. Но в чём? sniknik © > А список-то это кто будет формировать, Пушкин ?)
основной, там дел то будет на полсекунды... Сергей М. ©
> "Попросту говоря, когда поток А вызывает метод WaitFor потока
> B, сам он приостановливается, пока поток B не завершится "


А если поток A вызывает WaitFor уже ПОСЛЕтого как поток B успел завершиться и уничтожитьследы своего бытия, поток A при этом ничего не ждет - он немедля закругляется, сформировав перед этим отлуп ""Thread error: Неверный дескриптор (6)" sniknik © > Значит, дело в другом. Но в чём?
вот тут то и пригодится "переход на личности", т.к. рзумные доводы ты проигнорировал, а опровержения какие то не убедительные, из твоих же фантазий, а не реальности. YURY_ Сергей М., суть моего ответа в посте 98 Медвежонок Пятачок © Медвежонок Пятачок, если такая функция выполняется внутри потока, то она не приостанавливает поток - он выполняется в виде этой самой функции. Разве не так?

функция WaitFor у тебя внутри потока. Возврат из нее не сразу. Пока нет возврата, следующий код стоит.

функция formatdatetime тоже внутри потока может быть вызвана. Возврат из нее не сразу. Пока нет возврата следующий код стоит.

И чего? Сергей М. ©
> предположение о том, что первый поток завершился к моменту
> вызова WaitFor во втором потоке я опровергнул


Да нифига ты его не опроверг.
Именно это и происходит - никакая иная ситуация в приведенном коде не может привести к ошибке с кодом 6. YURY_ sniknik, Фантазии не причём. Есть пример с VCL (первый мой пост) и на его основе нетрудно убедиться, что дело вовсе не в том, что первый поток успевает завершиться до старта WaitFor второго. 1 минута времени для проверки. Но можно, конечно, попинять на фантазии человека, если нет идей. Тоже вариант. Медвежонок Пятачок © Не обижайте вы пацана.
Ему и так кот в отладчик нассал. sniknik © > и на его основе нетрудно убедиться
убеждаемся..
> FreeOnTerminate := True;
ой...
кранты убеждениям. YURY_ Сергей М., а вы попробуйте запустить пример. Увеличьте, скажем, значение 20000 на порядок. И убедитесь, что несколько минут второй поток терпеливо ожидает завершения первого. И как только он завершается - происходит исключение. Или вы полагаете, что второй поток несколько минут не стартует после Resume? Медвежонок Пятачок © procedure TForm1.Button1Click(Sender: TObject);
var
ThreadArray: Array of TMyThread;

К моменту выполнения магического кода внутри потоков массив уже мертв.
Дундук! Медвежонок Пятачок © А если бы он был жив, то настал бы черед замечаний приведенных выше. YURY_ Медвежонок Пятачок, почему массив мертв? Он умрет тогда, когда закроется главная форма. Не так ли? Медвежонок Пятачок © Медвежонок Пятачок, почему массив мертв? Он умрет тогда, когда закроется главная форма. Не так ли?

твой массив умрет сразу после :

....
ThreadArray[0].Resume;
ThreadArray[1].Resume;
end; Сергей М. ©
> дело вовсе не в том, что первый поток успевает завершиться
> до старта WaitFor второго


Дело именно в этом- сколько раз еще тебе это повторить чтобы ты наконец это понял ?)

Специально для фомы лениво-неверующего, не желающего прошарить исх-к WaitFor:

procedure TMyThread.Execute;
begin
if Assigned(ptrThread) then
 try
   ptrThread^.WaitFor;
 except
  on e: Exception do
    MessageBox(0, PChar(e.Message), 'ГРАБЛИ', mb_ok);    
 end;

MessageBox(0, 'Ты никогда не увидишь это сообщение, потому что FreeOnTerminate = True', 'НЕТ ГРАБЛЕЙ', mb_ok);    
for i := j to k do
  ptrEdit^.Text := IntToStr(i);
end; sniknik © > Увеличьте, скажем, значение 20000 на порядок.
время там не причем... оно в потоке УЖЕ и сразу прошло проверку
if Assigned(ptrThread) then
и ждет в WaitFor;, того момента, когда
> FreeOnTerminate := True;
второй поток будет разрушен. Anatoly Podgoretsky © > YURY_  (29.11.2011 14:20:28)  [88]

Тебе повторить чтоли OnTerminate YURY_ Медвежонок Пятачок, да, согласен. Ну пусть умирает. Он в данном примере не важен. Ок, массив умер. Но потоки-то живы! Им до этого массива дела нет. Так? Медвежонок Пятачок © Да у него вообще не тот код работает что приведен здесь.

Смотрите выше.
Есть массив - локальная переменная процедуры баттон1клик.
Есть код потоков, которые обращаются к полям масива.
Но нигде нет ни передачи массива, ни одноименных полей потоков.

А массив помирает сразу после выхода из баттон1клик.

Этот нехороший человек просто парит нам моск. app © Закрыто за безперспективностью


YURY   (2011-11-29 13:18:00) [2]

Медвежонок Пятачок, проблема в том, что заранее я не знаю сколько хранимок, какие из них и в какой последовательности выберет пользователь для выполнения своих потребностей. Это происходит на этапе выполнения. Поэтому я не могу заранее создать несколько разных классов потоков - каждый для своей хранимки. Потому и создаю один класс.


Anatoly Podgoretsky   (2011-11-29 13:17:00) [3]

> Сергей М.  (29.11.2011 13:11:53)  [53]

Судя по его описанию, у него чего то не в порядке.
А кеширование происходит и используется независимо от потоков.
Конечно я делаю все чтобы это произошло.


Anatoly Podgoretsky   (2011-11-29 13:12:00) [4]

> YURY  (29.11.2011 13:10:51)  [51]

Вместо того чтобы поискать где бутылочное горло, решил сделать велосипед с
трехугольными колесами


Сергей М.   (2011-11-29 13:11:00) [5]


> Anatoly Podgoretsky ©   (29.11.11 13:05) [48]


Ну тут некий выигрыш в произ-ти параллельно выполняемых сервером запросов возможно произрастает из врЕменного кеширования сервером многократно используемых клиентскими запросами наборов данных.


Медвежонок Пятачок   (2011-11-29 13:11:00) [6]

Ну если запускал, то включаем голову и думаем:

Что мы имеем?
Что надо сделать?

Имеем 20 хранимок.
1. Из них 10 можно запускать независимо от всего.
2. 3 процедуры нужно выполнить последовательно, причем первая из них - стартует независимо от всех других
3. Остальные тоже друг за другом, первая тоже независимо от всех.

Итого:

Нужно 10 + 2 потоков, в которые передаются списки процедур.
В первых десяти список будет из одного элемента.
В двух последних списки будут из трех и семи элементов.

запускаем потоки, потоки перебирают списки и запускают хранимки.

Все!


YURY   (2011-11-29 13:10:00) [7]

Anatoly Podgoretsky, Потоки запускаются на клиенте. Каждый поток инициирует выполнение хранимой процедуры на сервере. Процессор на клиенте вообще не причём. На сервере у нас 40 процессоров. Если я с клиента инициирую выполнение всех хранимых процедур на сервере последовательно, то общее время выполнения в несколько раз больше, чем при параллельном инициировании выполнения с помощью потоков.


Anatoly Podgoretsky   (2011-11-29 13:09:00) [8]

> sniknik  (29.11.2011 13:01:43)  [43]

Сервер или запросы не в порядке


Anatoly Podgoretsky   (2011-11-29 13:05:00) [9]

> YURY  (29.11.2011 12:58:40)  [40]

Моя практика и мониториг тоже показывает обратно, видно как потоки за
процессор дерутся и это не бесплатно.


YURY   (2011-11-29 13:05:00) [10]

Сергей М., совершенно верно.


YURY   (2011-11-29 13:04:00) [11]

Медвежонок Пятачок, действительно запустил. Параллельно. И они отлично работают. Гораздо быстрее, чем последовательно. Но в том и суть, что не все они должны запускаться параллельно, а чать из них - строго после окончания других. Это как идти в магазин, говоря по телефону и кушая мороженое - три дпроцедуры. Но совершить покупку (четвёртая процедура) можно лишь по окончании процедуры прихода в магазин.


Сергей М.   (2011-11-29 13:04:00) [12]


> Медвежонок Пятачок ©   (29.11.11 13:00) [42]


Не запускал он их)
Похоже он запустил несколько экземпляров клиентских процессов (на одном хосте или на разных - не суть как важно), дал каждому из них команду выпониль такую-то свою ХП и замерил время, за которое все эти процессы получат результаты своих запросов.


sniknik   (2011-11-29 13:03:00) [13]

> значит процедуры ты уже запускал из под потоков.
скорее из под процессов, но не говорит... а там параллельность от ядер "включается".


Anatoly Podgoretsky   (2011-11-29 13:03:00) [14]

> YURY  (29.11.2011 12:55:38)  [38]

Вот ты запустил два потока и ничего тебе не помешало.


sniknik   (2011-11-29 13:01:00) [15]

> Разница очевидна.
очевидно, что каждый поток у тебя работает в "полсилы", раз уж на другие время остается.
забрать "время ожидания" от других задач, это то как раз то для чего нужны потоки.


Медвежонок Пятачок   (2011-11-29 13:00:00) [16]

практика показывает обратное.

Ну и в чем тогда проблема?
Если практика показывает, значит процедуры ты уже запускал из под потоков.
Так?
Так.
Тогда в чем же проблема?


Сергей М.   (2011-11-29 12:58:00) [17]


> сервер у нас оракловый


А что, Оракл разве не позволяет запуск запросов на выполнение в неблок.режиме ?
Я просто не в курсе ..


> Очерёдность у них определена во входных параметрах


Да нашиша передавать параметром "очередность"-то ?
Зачем заставлять рабочие потоки выполнять несвойственную им задачу диспетчеризации ?


YURY   (2011-11-29 12:58:00) [18]

sniknik, практика показывает обратное.


Anatoly Podgoretsky   (2011-11-29 12:56:00) [19]

> YURY  (29.11.2011 12:53:35)  [35]

А у тебя не код, а сплошная ошибка


YURY   (2011-11-29 12:55:00) [20]

Anatoly Podgoretsky, почему вру? Что вы имеете в виду?


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо