Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > тупой нубский вопрос про потоки в Indy


Автор: CompWorm 26.4.2008, 20:42
у меня тупой нубский вопрос про потоки в Indy плз не бейте больно.

вот решил проверить, что делает инеец, если к нему приходит второй запрос, пока не выполнился первый... и наблюдал не совсем понятную картину:

Код

type
  TSimpleClient = class(TObject)
    IP,
    Name        : String;
    Thread      : Pointer; //тут храним указатель на поток клиента
  end;
var Clients  : TList;     //сюда записываем подсоединившихся клиентов

бла бла бла...

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  Msg    : String;   //сообщение
  i, z, z1: integer;
  F_main: TextFile;
begin
  {                     КЛИЕНТ ПРИСЛАЛ СООБЩЕНИЕ
   ну, первым делом, надо понять, кто это сообщение прислал...
   пробегаем по списку клиентов,
   в поисках указателя на поток, в котором пришло сообщение.
  }
  for i := 0 to Clients.Count - 1 do
  begin
    if Pointer(AContext)=TSimpleClient(Clients.Items[i]).Thread then
    begin
      { читаем, что прислал клиент }
      Msg:= AContext.Connection.IOHandler.ReadLn;
        if Msg='run' then begin
          randomize;
          z:=random(1000000);
          AssignFile(F_main, 'c:\'+inttostr(z)+'.txt');     
          try                                                        
          ReWrite(F_main);
          for z1:=0 to 100000000 do Write (F_main, 'Hello');                                 
          CloseFile(F_main);                                         
          except                                                     
          on EInOutError do ShowMessage ('Can''t Save your fucking file!');
          end;
        end;
      break; // впустую цикл не гоняем
    end;
  end;
end;


вот  smile 
так вот от сюда видно, что есть нечто под названием AContext, которое содержет нечто вроде потока, созданного самим INDY еще при подключении...

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

файлы пишутся одновременно, что говорит о том, что многопотоковость работает, но так же наблюдаются некоторые странности, к примеру, не отслеживается отключение клиентов, нет реакции при повторном присылании от того же клиента очередного сообщения итп, что говорит о том, что либо я не совсем понимаю многопоточность, либо индейцы кривовато многопоточат...

кто-нибудь может внести ясность в отношении 10 индейцев  smile 

Автор: CompWorm 27.4.2008, 17:41
ну, раз никто не отвечает, давайте разобьем вопрос на несколько простых...

правда ли что TIdTCPServer работает в отдельном потоке и создает для каждого подключившегося клиента по еще одному потоку внутри себя (вне главного VCL потока, но внутри своего) ?

user posted image

Автор: MetalFan 27.4.2008, 18:59
Цитата(CompWorm @  26.4.2008,  20:42 Найти цитируемый пост)
что либо я не совсем понимаю многопоточность, либо индейцы кривовато многопоточат

скорее первое.

Автор: CompWorm 27.4.2008, 19:12
не стану спорить, но если у кого-то тут все в порядке с многопоточностью, можите пояснить принцип работы плз?

и за одно как получается так, что сообщения не встают в очередь, и главное окно сервера не виснет даже во время длительной работы с файлом?

Автор: MetalFan 27.4.2008, 21:18
самая большая ошибка в приведенном тобой коде - небезопасная работа с разделяемыми данными...

Автор: CompWorm 28.4.2008, 09:14
MetalFan, 
наверно мой код не очень правильный и это мешает мне понять суть происходящего.
давайте тогда начнем с исправлений.
однако, я не очень большой спец в том, что делаю. поэтому, задавая вопросы, я пытаюсь повысить общий уровень своей эрудированности в данном вопросе.
но, ваши комментарии по поводу моей "ошибки в 17 строке" не позволяют мне сделать каких-либо качественных изменений в моем коде. 
не могли бы вы пояснить более детально, что в моем коде не так.

Автор: MetalFan 28.4.2008, 13:59
прочитай здесь на форуме статейку про многопоточность.
а более детально я тебе уже сказал - у тебя одновременная работа из нескольких потоков с одними и теми же данными

Автор: CompWorm 28.4.2008, 14:14
Цитата

прочитай здесь на форуме статейку про многопоточность.

она у меня уже в двух экземплярах распечатана)) прочел с удовольствием 2 раза, но видно не помогло  smile  )))

Цитата

а более детально я тебе уже сказал - у тебя одновременная работа из нескольких потоков с одними и теми же данными 


какими? файл один и тот же? так я специально рандомлю чтоб не совпадало 
Код

   randomize;
          z:=random(1000000);
          AssignFile(F_main, 'c:\'+inttostr(z)+'.txt');     
          try                                                        
          ReWrite(F_main);
          for z1:=0 to 100000000 do Write (F_main, 'Hello');                                 
          CloseFile(F_main);                                         
          except                                                     
          on EInOutError do ShowMessage ('Can''t Save your fucking file!');
          end;

есть вероятность, но пока ни разу не случилось повторов smile (я понимаю, что так нельзя, но этот код я сделал для простого теста, чтобы понять, как устроен компонент сервера в индейцах, и конечно не собираюсь вероломно использовать в своих программах) 

Автор: MetalFan 28.4.2008, 14:24
Цитата(CompWorm @  26.4.2008,  20:42 Найти цитируемый пост)
 for i := 0 to Clients.Count - 1 do

а это у тебя что?! где заполняется?

Автор: CompWorm 28.4.2008, 15:51
я представил кусок кода, ответственный за обработку приходящих сообщений (у меня вопрос про них). могу, если требуется выложить весь код.

MetalFan, а этот, я возможно плохо прокомментировал:
Цитата

var Clients  : TList;     //сюда записываем подсоединившихся клиентов

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

Автор: MetalFan 29.4.2008, 08:29
Цитата(CompWorm @  28.4.2008,  15:51 Найти цитируемый пост)
то есть, я мел в виду, что он заполняется при подключении новых клиентов в событии коннекта, чтобы был список присоединившихся клиентов.
соответственно, участок кода, который ты указал, не может быть выполнен минуя момент подключения, где список Clients  и пополняется очередной записью

т.е. ты считаешь, что "событие коннекта" не может произойти "одновременно" с выполнением данного кода? имо, заблуждаешься

Автор: CompWorm 29.4.2008, 12:18
 smile 
Код

...
 for i := 0 to Clients.Count - 1 do
  begin
    if Pointer(AContext)=TSimpleClient(Clients.Items[i]).Thread then
    begin
       {код, который НЕ может произойти, покуда в коннекте
         не пополнится список Clients типа TSimpleClient с указателем Thread: Pointer;
         на поток подключившегося клиента.
        }
    end;
   end;
...

Автор: dumb 29.4.2008, 13:30
Цитата(CompWorm @  28.4.2008,  15:14 Найти цитируемый пост)
прочел с удовольствием 2 раза, но видно не помогло
smile

не надо грозные смайлики вывешивать, лучше сконцентрируйся:
Цитата(CompWorm @  29.4.2008,  13:18 Найти цитируемый пост)
for i := 0 to Clients.Count - 1 do
пока ты в одном потоке крутишь этот цикл, может произойти подключение/отключение клиента, и твой список клиентов изменится. при этом цикл(когда выполнение к нему вернется) будет работать с несуществующими либо неполными данными.

Автор: CompWorm 29.4.2008, 14:03
Цитата

не надо грозные смайлики вывешивать, лучше сконцентрируйся:
Цитата(CompWorm @  29.4.2008,  13:18 Найти цитируемый пост)
for i := 0 to Clients.Count - 1 do
пока ты в одном потоке крутишь этот цикл, может произойти подключение/отключение клиента, и твой список клиентов изменится. при этом цикл(когда выполнение к нему вернется) будет работать с несуществующими либо неполными данными.

ха! а при отсоединении клиента происходит ожидание завершения потока, в котором выполняется уже неверный цикл... отличная заметка!
надо будет придумать защиту... к примеру, на каждый проход цикла поставить какую-нибудь проверку


я тут решил в мануалах индейцев порыться для просветления.
если вернуться к истокам темы, то я задавал, да простят меня админы, два вопроса:
- как устроена система потоков http://www.indyproject.org/docsite/html/frames.html?frmname=topic&frmfile=TIdTCPServer.html
- где находится и как выглядит очередь сообщений одного клиента http://www.indyproject.org/docsite/html/frames.html?frmname=topic&frmfile=TIdCustomTCPServer_Scheduler.html

если я правильно понимаю то, что тут написано,
- потоков всегда на один больше, чем клиентов. в частном случае, когда сервер только активировался, создаётся один поток для прослушивания.
- все сообщения клиента стоят в очереди не основного потока, а записываются и регулируются планировщиком  property Scheduler: TIdScheduler. поэтому основной поток не виснет при получении новых сообщений, пока не выполнилось первое.

Я ПРАВИЛЬНО ВСЁ ПОНЯЛ? ?

ПС соответственно, моя схема выше неправильная.

Автор: MetalFan 29.4.2008, 14:08
Цитата(CompWorm @  29.4.2008,  12:18 Найти цитируемый пост)
{код, который НЕ может произойти, покуда в коннекте
         не пополнится список Clients типа TSimpleClient с указателем Thread: Pointer;
         на поток подключившегося клиента.
        }

смотри сам. я тебе показал потокоНЕбезопасное место. считаешь, что все должно работать - аминь

Автор: CompWorm 29.4.2008, 14:11
dumb MetalFan , спасибо я понял свою ошибку в этом месте кода  smile 

скажите, насколько верно я все расписал в сообщении выше?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)