Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Подземелье Магов
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  06:57[Войти] | [Зарегистрироваться]

Обсуждение материала
Параллельные потоки
Полный текст материала


Другие публикации автора: Сергей Гурин

Цитата или краткий комментарий:

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


Важно:
  • Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
  • Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
  • При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
  • Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.



Добавить свое мнение.

Результаты голосования
Оценка содержания

  Содержит полезные и(или) интересные сведения
[1]1076.9%
 
  Ничего особенно нового и интересного
[2]215.4%
 
  Написано неверно (обязательно укажите почему)
[3]17.7%
 
Всего проголосовали: 13

Оценка стиля изложения

  Все понятно, материал читается легко
[1]555.6%
 
  Есть неясности в изложении
[2]444.4%
 
  Непонятно написано, трудно читается
[3]00%
 
Всего проголосовали: 9




Смотрите также материалы по темам:
[Потоки (нити) Threads] [Объекты синхронизации системы]

Комментарии жителей
Отслеживать это обсуждение

Всего сообщений: 35

15-09-2011 15:29
Написал на основе наборов классов автора небольшое приложение, за что ему большое спасибо. Но уткнулся в одну проблему, никак понять не могу, где затык. Суть приложения в следующем. Менеджер создает минимум два потока, время жизни каждого 30 минут. Создает не сразу, а с некоторой задержкой, чтобы когда один поток заканчивает работу, другой, более "младщий" еще мог обрабатывать запросы, пока завершается "старший" и создается новый вместо него. Таким образом создается несколько потоков, которые обеспечивают непрерывность сервиса. Так вот, пока существует только один поток, после старта программы, пока не стартовал второй, первый прекрасно обслуживает запросы, как задумано. Но после того как создается второй поток, начинаются проблемы. Первый поток перестает обрабатывать запросы, такое ощущение, что он находится в остановленном состоянии. Процедура, которая определяет свободный, ожидающий данные поток и передает ему параметры, ниже:

procedure TDataThreadManager.PutRequestToChannel(SocketHandle: TSocket; Code: String);
var
  hash: Integer;
  th: TGsvThread;
  AllBusy: boolean;
begin
  AllBusy := True;
  EnterCriticalSection(FLatch);
  try
    for hash := Low(FHashTable) to High(FHashTable) do begin
      th := FHashTable[hash];
      if Assigned(th) then begin
        th.Suspend;
        AllBusy := TMyThread(th).Busy;
        if not AllBusy and not TMyThread(th).Terminated then begin
          TMyThread(th).Code := Code;
          TMyThread(th).InitialSocketHandle := SocketHandle;
        end;
        th.Resume;
        if not AllBusy then Break;
      end;
    end;
  finally
    LeaveCriticalSection(FLatch);
  end;
  if AllBusy then Add(TMyThread.Create(SocketHandle, Code, 3), True);
end;

Метод Execute у потока:
procedure TMyThread.Execute;
begin
  try
    while not Terminated do begin
      if FSessionID = '' then Initialize;
      if Code <> '' then GetData;
      Sleep(50);
    end;
  except
    Terminated := True;
  end;
end;

Когда отрабатывает GetData, она присваивает Code := '', и поток ожидает дальнейшего поступления данных. Вот я и не пойму, в чем проблема. Когда он один, то все отлично работает. Но как только создается второй, то все, первый "засыпает". Отлаживал эту ситуацию в PutRequestToChannel, после присваивания th := FHashTable[hash] смотрю свойства потока, когда несколько раз пытаюсь передать в него значение Code, а там так и сидит предыдущее значение, т.е. поток не работает, будто паузе висит. Что тут может быть, как думаете, товарищи?


13-11-2007 12:00
сообщение от автора материала
Я думаю, что это неудачное решение - выполнять какие-либо значимые действия в завершающем событии. Это событие предназначено только для того, чтобы корректно завершить поток, а точнее, освободить те ресурсы, которые не связаны с памятью (например, открытые файлы). Мне кажется, что лучше разбор файла делать либо сразу после его загрузки, либо в отдельном потоке, либо поставить его в очередь и обработать в основном потоке


13-11-2007 05:18
Спасибо за ваш ответ!
Простите, но я похоже не понимаю как это сделать в моем случае :(
Есть простенький класс для получения файла из интернета.

constructor TGetInetFileThread.Create(const URL: string; Delay: Cardinal = 0);
begin
  inherited Create;
  FURL := URL;
  FDelay := Delay;
  FDownloaded := False;
  HTTP := THTTPSend.Create;
end;

destructor TGetInetFileThread.Destroy;
begin
  HTTP.Free;
end;

procedure TGetInetFileThread.Execute;
begin
  if FDelay > 0 then Pause(FDelay);
  HTTP.HTTPMethod('GET', FURL);
  FDownloaded := True;
end;



И в классе-потомке переопределяется метод OnFinished:
TAppUpdateThread = class(TGetInetFileThread)
public
  procedure OnFinished; override;
end;

При завершении приложения вызывается TerminateAll в OnCloseQuery

procedure TSpiderUpdateThread.OnFinished;
begin
  if Downloaded then // Downloaded = True
  begin
    // parse downloaded file
  end;
end;

Причем OnFinished срабатывает сразу же, но потом приложение еще некоторое время не завершается, как будто ждет завершения HTTP.HTTPMethod('GET', FURL);


12-11-2007 21:33
сообщение от автора материала
Самостоятельное или внешнее завершение потока - это логически эквивалентные события. Если Вам требуется их различение, то в классе потока Вам нужно определить булеву переменную, которую поток будет явно устанавливать при самостоятельном завершении


12-11-2007 16:43
Подскажите, пожалуйтса, как узнать в OnFinished завершился ли поток сам или его уничтожили? (и Finished, и Terminated возвращают True)


01-04-2007 01:14
сообщение от автора материала
Вы пишете: я не первый кто попросил от вас хотя бы один банальный пример применения вашей библиотеки многопоточности, который бы показал, всю ее красоту и способности.

Еще раз повторю свой ответ, сделанный несколькими сообщениями ранее: Эта работа является продолжением моей более ранней работы (библиотека Gala http://gurin.tomsknet.ru/gala.html). Библиотека Gala хорошо документирована, к ней прилагается много примеров и демонстрационных файлов.

Я не вижу смысла дублировать примеры и поэтому отсылаю к своей предыдущей работе.


30-03-2007 14:56
мдя....

Я вот что заметил, при использовании потоков  TGsvThread  выполняемая программа начинает дико жрать файл подкачки, пока не сожретего до того моента, когда система скажет - все, память кончилась. Ради эксперемента TGsvThread заменил на обычный TThread, и файл подкачки перестал расти и замедлять работу программы. Полчему это происходит?

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


30-03-2007 10:05
сообщение от автора материала
К сожалению, Вы совсем неправильно понимаете сущность потоков. Потоки являются автономными параллельными (а точнее, квазипараллельными) сущностями, которые выполняются каждый сам по себе. То есть, всё что делает поток, он должен делать САМОСТОЯТЕЛЬНО, например, устанавливать значения собственных переменных, считывать данные из разделяемых контейнеров, взаимодействовать с другими параллельными потоками. Другие потоки могут только взаимодействовать с ним, вызывая его методы (иными словами - посылая ему сообщения). НИ В КОЕМ СЛУЧАЕ не следует из одного потока пытаться что-то изменить в другом потоке, ту же переменную spTxtFile. Вы можете передать ее, например, как аргумент конструктору Вашего потока. Частично поток может инициализировать свои данные в конструкторе (когда он выполняется в контексте родительского потока), частично перед началом своего основного цикла (уже в своем контексте) и все остальные изменения - в теле своего основного цикла. Получать дескриптор потока нужно только тогда, когда Вы выполняете взаимодействия между потоками. Например, если Вы хотите поместить передать потоку некоторые данные, Вы можете создать в нем защищенную очередь входных данных. Другие потоки ни к коем случае не должны самостоятельно работать с этой очередью - вместо этого в классе потока Вы должны предусмотреть метод, который сначала блокирует собственную очередь (с помощью критической секции), затем выполняет операции с очередью, и затем разблокирует очередь. У параллельного программирования есть свои законы, они существенно отличаются от последовательного программирования и, если Вы хотите создать работающую (неглючную) программу, Вы должны учитывать эти законы. Это относится не только к классу TGsvThread - это вообще свойственно любой многопоточной программе.


30-03-2007 07:25
С этим все понятно. Но опять получается проблема
Рабираем код:

// создаю свой клас потока на основе TGsvThread
type
  TDBThread = class(TGsvThread)
       private
            ...
      public
          spTxtFile : String;
           ...
  protected
    procedure Execute; override;
  end;
...
//объявляю переменную и создаю менеджер потоков
pManager : TGsvThreadManager;
pManager := TGsvThreadManager.Create(64);
...
//создаю орстановленный поток
Handle := pManager.Add(TDBThread.Create, false);

// теперь мне нужно в созданном потоке установить значения переменных
// например spTxtFile : String;

//  ВОТ ТУТ ПРОБЛЕМА!

//запустить поток
Thread.Resume;




Если использовать конструкцию Thread := ThreadManager.Lock(Handle);
в моем случае это будет Thread := pManager.Lock(Handle);
но переменная Thread должна быть типа TGsvThread, и никак не TDBThread, соответственно я немогу достучаться до переменной spTxtFile : String;

Как быть в этом случае?







29-03-2007 09:52
сообщение от автора материала
Не станет никогда в принципе. Вопросы уничтожения потоков объясняются в статье очень подробно. Вот пример кода из статьи

// создание потока
  Handle := ThreadManager.Add(TSomeThread.Create);
  ....
  // работа с потоком
  Thread := ThreadManager.Lock(Handle);
  if Assigned(Thread) then
  begin
    ....
  end;
  ....
  // уничтожение потока
  ThreadManager.Release(Handle);


Наличие (или отсутствие)переменной, ссылающейся на поток, никак не связано с временем жизни потока - им нужно управлять явно с помощью Lock и Release (а не наличием или отсутствием переменной). При создании потока и добавлении его к диспетчеру, счетчик ссылок становится равным 1. Для уничтожения потока нужно вызвать метод Release для дескриптора потока (но не уничтожать сам объект). Если у потока появляется еще один пользователь, то этот пользовать должен вызвать метод Lock, а после того, как поток стал ему не нужным - метод Release. Пользователей у потока может быть множество и поток не будет уничтожен до тех пор, пока все пользователи не вызовут метод Release данного потока. Еще раз подчерку - не желательно вообще где-то держать переменные, ссылающиеся на поток - для этого существуют дескрипторы. Причина, по которой используются дескрипторы (а не ссылки на объекты потоков) очень подробно разъясняется в статье


29-03-2007 01:43
Еще несколько вопросов к  Сергею Гурину:

Саздается поток

myPotok := myThread.Create;


потом передается в менеджер потоков

myMan.Add(myPotok);



1) В какой момент переменная myPotok станет (станет ли вообще) равная nil?


27-03-2007 05:32
Lord_Driver!

С вопросами, не касающимися статьи, добро пожаловать на Круглый стол! Если вы будете продолжать здесь, ваши сообщения будут удалены.


27-03-2007 04:55
Заработало!!! после установки другой версии сервера - Firebird-2.0.1.12855-1-Win32. Пока пробовал на 12 потоках, созданых "вручную", тоесть с жесткими параметрами что их 12 и каждому указан отдельный текстовый файл.

Но теперь проблема другая, некая особенность сервера Firebird (отметил это и на 1.0 и на 2.0), дело в том, что когда база достигает определенного размера, метров так 40,  он резко теряет производительность, если вначале он кушал до 50% от ЦП, то к моменту размера базы в 40 метров, он кушает всего 10 - 15% от ЦП и соответственно падает его производительность.  

Вопрос конешно не по теме, но может кто сталкивался, почему это происходит, и как Firebird сохранять производительность?


27-03-2007 02:33
Непонятно но факт.

Написал кусок кода, в котором потоки пока создаются руками (примерно так же как и в указанном мной уже тут коде).

В потоке реализовано чтение файла по строчно. Два потока очень даже резво читают данные из двух файлов паралельно. Казалось бы все хорошо, но тут возникает другая проблема, почемуто при соединении с базой второго потока, все вешается. Переменные для БД  (Firebird-1.0.3.972-Win32) соединения созданы внутри процедуры обработки файла и перекрещиватся ну ни как не должны. Я пока в ступоре, незнаю что влияет на потоки, когда второй поток, пытается подцепится к базе.


26-03-2007 14:11
сообщение от автора материала
> Вопрос, насколько опасно не терминировать потоки при закрытии формы в случае если они отработали?

Если поток отработал, то это автоматически означает его завершенность и то, что он будет уничтожен менеджером потоков (конечно, если на поток больше никто не ссылается). То есть, уничтожение потоков - это забота менеджера. Незавершенные потоки при закрытии формы нужно завершать с помощью метода TerminateAll менеджера. Процедура завершения состоит в том, что менеджер сначала уведомляет незавершенные потоки о необходимости конце работы (устанавливая свойство Terminate), но потоки должны самостоятельно отреагировать на свое завершение и выполнить корректную остановку.

> Каким способом можно организовать слежение за количеством одновременно запущенных потоков. и при освобождении потока (окончание обработки файла) запускать еще один.

Это классическая задача связанная с ограничением числа ресурсов. Наиболее адекватно она решается использованием счетного семафора. В этой статье семафоры не описаны, но их описание можно найти в любой книге по программированию в Windows или в моей статье о библиотеке Gala (gurin.tomsknet.ru/gala.html). Если не хотите разбираться с семафорами, то примитивно простое решение  - периодически (например, по таймеру) получать от менеджера список активных потоков. Размер списка даст Вам число активных потоков. В общем же случае от таких решений стоит воздерживаться и использовать семафоры. Я специально сделал свойство Count менеджера скрытым, чтобы затруднить выполнение подобных некрасивых решений.


26-03-2007 13:37
Теперь обрисую кратко задачу, для которой нужна многопоточность:

Есть огромное количество текстовых файлов, которое нужно перелопачивать и запихивать в базу данных. Вот именно для этого и нужна многопоточность!!!

Ход решения простой, но доконца пока не дожат.
Нужно сканировать каталог, при наличии в нем файла, обработка файла запускается в потоке, но тут сразу появляется ограничение, одновременно обрабатываемых файлов, соответственно и потоков, должно быть строго фиксированное количество, скажем 255.
Файлов же в каталоге может быть до 3000 и более. То есть нужен один поток, который запускает обработку файлов в других потоках и следит чтобы одновременно небыло запущено более 255 потоков, т.к. если это делать не в потоке, форма будет неуправляемой и приложение тормазнутым.

А вот теперь вопрос к автору: Каким способом можно организовать слежение за количеством одновременно запущенных потоков. и при освобождении потока (окончание обработки файла) запускать еще один.

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

А вообще Автору огромное спасибо за предложенное решение по потокам.


26-03-2007 13:36
Сразу извинюсь, что немного поленился и не доконца разобрался.
И еще не растолковал саму задачу, ради которой нужна многопоточность.

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

И так, создаем свой клас унаследованный от TGsvThread в котором и переопределим метод Execute. Полчается следующее:

unit MyPotok;

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

type
  TDBThread = class(TGsvThread)
  private
    { Private declarations }

  public
  iNum : Integer;
  procedure WriteLog(sLogStr :String);

  protected
    procedure Execute; override;
  end;

implementation

uses FMain;

procedure TDBThread.WriteLog(sLogStr :String);
Begin
sLogStr := IntToStr(iNum) + '->' + sLogStr;
//PostMessage(FormMain.Handle, WM_THREAD_COUNTER, 0 , Integer(PChar(sLogStr)));
  SendMessage(FormMain.Handle, WM_THREAD_LOG, 0 , Integer(PChar(sLogStr)));
End;

procedure TDBThread.Execute;
var
  i: Integer;
begin
  WriteLog('= Начало работы потока');
  for i:=1 to 1000 do
   begin
      WriteLog('Test ' + IntToStr(i));
   end;
  WriteLog('= Окончание работы потока');
end;

end.



Затем реализуем использование на форме:

unit FMain;

interface

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

const
  WM_THREAD_LOG = WM_USER + 1000;

type
  TFormMain = class(TForm)
    Memo1: TMemo;
    Start: TButton;
    procedure StartClick(Sender: TObject);
  private
    { Private declarations }

  public
    { Public declarations }

    procedure OnThreadLog(var aMessage: TMessage);
    message WM_THREAD_LOG;

  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}
procedure TFormMain.OnThreadLog(var aMessage: TMessage);
Begin
Memo1.Lines.Add(pChar(aMessage.LParam));

End;


procedure TFormMain.StartClick(Sender: TObject);
var
pMan : TGsvThreadManager;
pPotok, pPotok1, pPotok2, pPotok3 : TDBThread;
begin
pMan := TGsvThreadManager.Create(1);
pPotok := TDBThread.Create;
pPotok.iNum :=1;
pMan.Add(pPotok);

pPotok1 := TDBThread.Create;
pPotok1.iNum :=2;
pMan.Add(pPotok1,true);

pPotok2 := TDBThread.Create;
pPotok2.iNum :=3;
pMan.Add(pPotok2,true);

pPotok3 := TDBThread.Create;
pPotok3.iNum :=4;
pMan.Add(pPotok3,true);

end;

end.



Все очень замечательно работает!!!

Кстати:
1) Как вы заметили в коде встречается закоментированная строка:

//PostMessage(FormMain.Handle, WM_THREAD_COUNTER, 0 , Integer(PChar(sLogStr)));


Если использовать PostMessage то в Memo1 строки появляются по порядку от каждого потока (1 1 1 ... 2 2 2 ... 3 3 3 ... ...), а при SendMessage в разноброд (1 2 3 ... 1 3 2 ... 3 2 1 ...)
2)Получилось передавать текст через LParam и вполне работает.

3) Вопрос, насколько опасно не терминировать потоки при закрытии формы в случае если они отработали?




26-03-2007 11:01
сообщение от автора материала
1) Если посмотреть класс TGsvThread, то можно увидеть единственный абстрактный метод - Execute. Именно в этом методе производный от TGsvThread класс должен определить свой исполняемый код. В статье приведен пример переопределения этого метода:

procedure TDerivedThread.Execute;
begin
  // действия до начала цикла
  ....
  // циклическое выполнение
  while not Terminated do begin
    ....
  end;
end;



Поскольку класс TGsvThread содержит абстрактный метод, то по определению Вы не должны создавать его экземпляры. К сожалению, компилятор Delphi разрешает конструирование таких классов, но при вызове абстрактного метода возникает ошибка времени выполнения.

В своем примере Вы переопределили внутреннюю, скрытую функцию, основное назначение которой как раз и состоит в том, чтобы вызывать метод Execute у конкретного экземпляра класса. Функция ThreadProc
одна-единственная на все процессы, а конкретика каждого конкретного процесса описывается в Execute.

2) Напрямую поток ни в коем случае не должен взаимодействовать с VCL-компонентами. Это к Вашему вопросу "НЕСКОЛЬКО ПОТОКОВ ОДНОВРЕМЕННО ПИШУТ В Memo1 СТРОКИ ОТ 1 ДО 100. Уж очень сильна нужна многопоточность".

Для того, чтобы обойти принципиальную однопоточность VCL, используются различные средства. Стандартный компонент Delphi TThread предлагает для этого метод Synchronize. Класс TGsvThread использует для взаимодействия с VCL другой механизм события Windows - этому посвящен целый раздел в статье - он называется "Взаимодействие с VCL-потоком". Судя по Вашему вопросу Вам удобнее будет использовать именно стандартный TThread, так как реализация взаимодействия с VCL в нем реализуется проще. TGsvThread более удобен при динамическом создании и уничтожении множества потоков, взаимодействующих друг с другом, то есть, это несколько другая ниша.


26-03-2007 08:12
Доброго времени суток, прочитал статью, посмотрел код, но так и не понял, где должен находится основной код, который должен выполняться в потоке.
На первый взгляд вроде тут:

function ThreadProc(p: TGsvThread): Integer;
var
i:integer; {добалено мной}
begin
  // Процедура, которую вызывает операционная система при старте параллельного
  // потока
  Result := 0;
  with p do begin
    // жизненный цикл потока
     for i:=1 to 100 do Form1.Memo1.Lines.Add(IntTostr(i) + ' - test'); {длбалено мной}
    ThreadExecute;    { - ВОТ ЭТОТ ВЫЗОВ МНЕ НЕ ПОНЯТЕН}
    // самозавершение потока
    if Assigned(FManager) then
      FManager.Release(FGsvHandle);
  end;
  EndThread(0);
end;



Вроде даже работает, если написать вот такой вызов:

procedure TForm1.Button1Click(Sender: TObject);
var
pMan : TGsvThreadManager;
pPotok, pPotok1, pPotok2, pPotok3 : TGsvThread;
begin
pMan := TGsvThreadManager.Create(1);

pPotok := TGsvThread.Create;
pPotok.iNum :=1;
pMan.Add(pPotok,true);

pPotok1 := TGsvThread.Create;
pPotok1.iNum :=2;
pMan.Add(pPotok1,true);

pPotok2 := TGsvThread.Create;
pPotok2.iNum :=3;
pMan.Add(pPotok2,true);

pPotok3 := TGsvThread.Create;
pPotok3.iNum :=4;
pMan.Add(pPotok3,true);

end;



Но я неуверен, что тут все корректно использую.


ХОТЕЛОСЬ БЫ ВСЕ ТАКИ ПОСМОТРЕТЬ НА КАКОЙ НИБУДЬ МАЛЕНЬКИЙ ПРИМЕР.
СКАЖЕМ НЕСКОЛЬКО ПОТОКОВ ОДНОВРЕМЕННО ПИШУТ В Memo1 СТРОКИ ОТ 1 ДО 100.

Уж очень сильна нужна многопоточность.


21-01-2007 05:28
Спасибо, буду разбираться.


18-01-2007 05:29
сообщение от автора материала
Эта работа является продолжением моей более ранней работы (библиотека Gala http://gurin.tomsknet.ru/gala.html). Библиотека Gala хорошо документирована, к ней прилагается много примеров и демонстрационных файлов. Данную статью я дополнять не предполагал.


18-01-2007 05:08
Очень интересный материал, спасибо.
Уважаемый автор, а не могли бы вы дополнить статью простыми примерами?


25-12-2006 04:42
сообщение от автора материала
К сожалению, я не знаком с набором компонентов Jv Threading. Гурин.


25-12-2006 04:27
Можно вопрос?  В панели компонент от JEDI есть закладка Jv Threading
В ней 12 компонент, в том числе и какие-то manager'ы
Они реализуют эту функциональность, про которую вы написали?


02-10-2005 10:11
сообщение от автора материала
Вы правы, пакет Gala в большей степени ориентирован на относительно небольшое количество интенсивно взаимодействующих потоков, причем время существования потоков относительно велико. Пакет GsvThread ориентирован на относительно большое число потоков с существенно различающимся временем жизни, причем взаимодействия потоков достаточно просты. В пакете Gala время жизни объектов, инкапсулирующих потоки, заканчивается только с завершением программы, в то время, как в пакете GsvThread время жизни потока определяет подсчетом ссылок, то есть поток живет до тех пор, пока он нужен хотя бы одному другому потоку. Кроме того, накладные расходы на поддержание механизма рандеву в пакете Gala относительно велики, но, с другой стороны, программировать взаимодействия очень удобно. В пакете GsvThread исключены эти накладные расходы, но вместо рандеву реализованы относительно простые каналы.


01-10-2005 19:29
У Вас на сайте помимо данной библиотеки нашел пакет Gala...
Как мне показалось, gsvThreads больше ориентирован на большое количество динамически создаваемых потоков, а Gala на их взаимодействие. Верно?

Точнее, предполагалось ли вобще какое-то разграничение их по сферам применения? :-)


16-04-2005 11:16
С Вашими замечаниями в принципе можно согласиться. Не оправдываясь, я попробую объяснить их причину.

"Весьма сомнительно использование трюка доступа к приватным свойствам и методам чужого класса в рамках одного юнита". Я бы не стал классифицировать это как трюк, более того, мне эта практика кажется вполне оправданной - и именно по этой причине в Delphi введена видимость свойств в пределах юнита, а в C++ это делается с помощью ключевого слова friend. Причина для такого поведения кроется именно в желании скрыть приватные свойства классов и не делать их общедоступными, но в тоже время, тесно связанные классы, такие как элементы и контейнеры, элементы и менеджеры обязательно должны быть осведомлены о внутренних особенностях. Если же этого не делать, то придется состояния объектов открывать, что еще хуже. Поэтому локальное использование видимости в пределах юнита мне кажется меньшим из зол.

"некоторые вызовы функций Windows API оформены как вызовы процедур (не проверяется код возврата)". В некоторых случаях он действительно просто не нужен, но в тех случаях, когда код важен, он всегда проверяется. Хотя, в общем случае, Вы правы.

"нельзя забывать про хэш, который при превышении Capacity может/будет совпадать для разных хэндлов, необходимо сравнивать дополнительно хэндлы и, при необходимости, лезть рекурсивно в Collision". Эта ситуация решается крайне просто назначанием достаточно большого для данной задачи размера массива. Работа с коллизиями - абсолютная неизбежность, которая в данном случае существенно облегчается из-за того, что хеш-коды распределены очень равномерно.

"некрасиво (с моей точки зрения) выглядит многократное использование public в определении нескольких классов - сначала public для конструкторов, потом пошла приватная часть и т.п., а потом опять public". Здесь я могу возразить следующее - мне крайне не нравится упорядоченность имен по алфавиту - я предпочитаю логическую упорядоченность полей, методов и свойств. Кроме того, мне представляется гораздо более логичным размещать код методов в том же порядке, в каком методы объявлены в классе. С этой точки зрения логично определять конструктор и деструктор. Если Borland так не делает - это выбор его программистов, но я буду использовать именно такой порядок деклараций и реализаций, нравится это кому-либо или нет.

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


14-04-2005 04:30
последний комментарий был мой.


14-04-2005 04:27
1. Весьма сомнительно использование трюка доступа к приватным свойствам и методам чужого класса в рамках одного юнита. Нужно заботиться о девейлоперах, которым может понадобиться функциональность, которая спрятана в private методах (см. использование TGsvThread.Terminate).

2. некоторые вызовы функций Windows API оформены как вызовы процедур (не проверяется код возврата/успешность вызова).

3. интересно (но спорно!) реализовано хранение ссылок на экземпляры TGsvThread в динамическом массиве. Играться с Capacity невозможно по ходу дела, хотя Collision реализован вполне нормально.
Однако для писателей, которые будут перекрывать менеджер, эта вещь будет представлять собой опасную штучку (простыми словами - геморрой, простите), про которую нельзя забывать (в смысле нельзя забывать про хэш, который при превышении Capacity может/будет совпадать для разных хэндлов, необходимо сравнивать дополнительно хэндлы и, при необходимости, лезть рекурсивно в Collision).

4. некрасиво (с моей точки зрения) выглядит многократное использование public в определении нескольких классов - сначала public для конструкторов, потом пошла приватная часть и т.п., а потом опять public.

5. не "по стандарту" производится вызов событий.
procedure TGsvThreadManager.Release(aHandle: Integer);
..
    if (FCount = 0) and Assigned(FOnEmpty) then
      FOnEmpty(Self);
...
вместо этого принято вызывать виртуальный protected метод DoEmpty
procedure TGsvThreadManager.DoEmpty;
begin
  if Assigned(FOnEmpty) then
    FOnEmpty(Self);
end;
а в TGsvThreadManager.Release написать
    if (FCount = 0) then
      DoEmpty;
Опять же - забота о разработчиках, которые захотят модифицировать менеджер для своих целей.

Резюме:
- для новичков в программировании, этот набор классов, по моему, сложноват.
- а на эдвансед, которые желают унаследоваться и модифицировать функциональность - не достаточно готов к этому.
Сообщение не подписано


08-04-2005 09:18
сообщение от автора материала
Последний проект, в котором я использовал приведенную разработку, представляет собой клиент-серверную систему, в которой удаленные клиенты соединяются с сервером, имеют возможность взаимодействия между собой, и, кроме этого, могут создавать параллельные потоки, реализующие интерпретируемые сценарии, причем другие клиенты могут взаимодействовать с потоками сценариев. То есть, получается около сотни параллельных потоков, которые взаимодействуют между собой. Естественно, что потоки могут завершаться самостоятельно и для сервера никак не допустима ситуация, при которой один поток обратится к уже уничтоженному другому потоку. Сложность состоит в том, что связи потоков исключительно динамические, и инициируются по ходу развития сценариев или при широковещательных взаимодействиях, когда один клиент отправляет сообщения всем или части других клиентов. Система используется для мониторинга процессов и опережающего моделирования возможных ситуаций, протекающих в энергосистеме. Сервер рассчитан на постоянную работу (хотя, конечно, его нельзя отнести к программам реального времени).


08-04-2005 04:20
Статья хорошая, мне понравилась, вот бы еще привести ситуации в которых это надо использовать, было бы еще лучще. Я тоже работаю с потоками, но у меня все очень просто и скорость переключений не важна. Пишу ThreadManager только для более удобного слежения за потоками и их визуального наблюдения. Пока сделал цепочку наследников от TThread для базоваго потока и небольшой менаджер. Возможно действительно стоит попробовать переписать уничтожение потоков, у меня поток может уничтожить сам себя и после проинформировать об этом ThreadManager. Я понимаю, что в этой ситуации есть возможность колиции, но кажется она у моей ситуации стремится к нулю. Хотелось бы знать когда стоит применять токой сложный подход?


29-03-2005 02:17
> WM_THREAD_COUNTER = WM_USER + 1000;
Лучше использовать в качестве базы WM_APP.

Гарантируется, что значения из диапазона WM_APP (0x8000...0xBFFF) системой точно не используются.

WM_USER - это не значит "юзай, юзер"; это значит, что коды из этого диапазона используются системной user32.dll.
-------------------------------------------------
Как насчёт компонент MTThreading (Managed threading) из библиотеки JEDI (The Initial Developer of the Original Code is Erwin Molendijk. Portions created by Erwin Molendijk are Copyright (C) 2002 Erwin Molendijk)?
Сообщение не подписано


25-03-2005 09:17
сообщение от автора материала
Да, Вы просто немного иначе записали условие проверки произошедшего события. Это, в общем-то, дело вкуса. К потокам это не имеет никакого отношения


25-03-2005 08:39
ой! Общибся. Конечно, же

function TGsvSelect.Wait(aTimeout: Cardinal): Boolean;
var
  res, i: Cardinal;
begin
  res    := WaitForMultipleObjects(FCount, @FEvents[0], False, aTimeout);
    Result := res > WAIT_OBJECT_0 AND
              res < (WAIT_OBJECT_0 + FCount);
    if Result then begin
      i := res - WAIT_OBJECT_0;
      if Assigned(FMethods[i]) then
        FMethods[i]();
    end;
end;



25-03-2005 08:37
Наверное, потому, что я пока новичек в дельфи, въехал не во все. Буду перечитывать еще раз (когда дочитаю). В целом статья великолепная...

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


function TGsvSelect.Wait(aTimeout: Cardinal): Boolean;
var
  res, i: Cardinal;
begin
  Result := False;
  res    := WaitForMultipleObjects(
          FCount, @FEvents[0], False, aTimeout);
  if res < (WAIT_OBJECT_0 + FCount) then begin
    Result := res > WAIT_OBJECT_0;
    if Result then begin
      i := res - WAIT_OBJECT_0;
      if Assigned(FMethods[i]) then
        FMethods[i]();
    end;
  end;
end;



можно ли исправить на


function TGsvSelect.Wait(aTimeout: Cardinal): Boolean;
var
  res, i: Cardinal;
begin
  res    := WaitForMultipleObjects(
        FCount, @FEvents[0], False, aTimeout);
  if  then begin
    Result := res > WAIT_OBJECT_0 AND
              res < (WAIT_OBJECT_0 + FCount);
    if Result then begin
      i := res - WAIT_OBJECT_0;
      if Assigned(FMethods[i]) then
        FMethods[i]();
    end;
  end;
end;




Если я правильно понимаю, то, что я поправил никак не отразится на данном коде, и не имеет никакого отношения к потокам... (?)



Прошу прощения, если это оффтопик.


Добавьте свое cообщение

Вашe имя:  [Войти]
Ваш адрес (e-mail):На Королевстве все адреса защищаются от спам-роботов
контрольный вопрос:
Два кольца, два конца, посередине гвоздик.
в качестве ответа на вопрос или загадку следует давать только одно слово в именительном падеже и именно в такой форме, как оно используется в оригинале.
Надоело отвечать на странные вопросы? Зарегистрируйтесь на сайте.

Оценка содержания
 
Содержит полезные и(или) интересные сведения
 
Ничего особенно нового и интересного
 
Написано неверно (обязательно укажите почему)


Оценка стиля изложения
 
Все понятно, материал читается легко
 
Есть неясности в изложении
 
Непонятно написано, трудно читается

Текст:
Жирный шрифт  Наклонный шрифт  Подчеркнутый шрифт  Выравнивание по центру  Список  Заголовок  Разделительная линия  Код  Маленький шрифт  Крупный шрифт  Цитирование блока текста  Строчное цитирование
  • вопрос Круглого стола № XXX

  • вопрос № YYY в тесте № XXX Рыцарской Квинтаны

  • сообщение № YYY в теме № XXX Базарной площади
  • обсуждение темы № YYY Базарной площади
  •  
     Правила оформления сообщений на Королевстве
      
    Время на сайте: GMT минус 5 часов

    Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
    Функция может не работать в некоторых версиях броузеров.

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования