Форум: "Сети";
Текущий архив: 2006.05.21;
Скачать: [xml.tar.bz2];
ВнизКак грамотно организовать копирование файлов по локалке. (+) Найти похожие ветки
← →
EarlVadim © (2006-01-18 10:42) [0]Есть необходимость периодически опрашивать удалённые хосты (с доступом естессно) на наличие в сети, и в положительном случае, копировать с них определённый файл.
Сейчас тупо используеься COPYFILE(SFN,TFN); Проблема в том, что процесс копирования файла "примораживает" программу, а если хост недоступен, то это уже на 5-10 секунд "заморозка". Ни свернуть, ни закрыть, ничего. Файл небольшой - до 100Кб.
Как это НАДО делать, чтобы процесс опроса-копирования действительно был ФОНОВЫМ?
← →
EarlVadim © (2006-01-18 10:45) [1]Да, чуть не забыл, Сеть- MS на TCP/IP
← →
Fay © (2006-01-18 11:37) [2]2 EarlVadim © (18.01.06 10:42)
Вытащи в отдельный поток.
← →
EarlVadim © (2006-01-18 11:39) [3]Не сталкивался.
Если не в лом, как делать?
← →
Fay © (2006-01-18 11:45) [4]2 EarlVadim © (18.01.06 11:39) [3]
CreateThread | свой наследник TThread
← →
EarlVadim © (2006-01-18 12:41) [5]2 Fay © (18.01.06 11:45) [4]
Я тут в сети нашёл пример создания приложения с двумя потоками.
http://rusasdatabase.narod.ru/threads.html
Все работает, все ОК. Но вот вопрос, как прикрутить это все себе.
Объясните принцип. Вся программа должна быть одним потоком, а CopyFile - вторым, Или просто создать в программе один поток с CopyFile?
← →
Digitman © (2006-01-18 17:32) [6]реализуй код доп.потока, который собственно и будет заниматься копированием файла.
ссылку на местоположение файла передавай параметром при конструировании потока
← →
EarlVadim © (2006-01-19 09:37) [7]2 Digitman © (18.01.06 17:32) [6]
Ну с принципом я в общем-то разобрался.
Простая прога с потоком - работает.
Вставляю теже самые компоненты, процедуры, классы в свою программу, поток не работает. (Хотя в теле потока для начала все предельно просто)
unit MyThreadU;
interface
uses
Classes, Windows, SysUtils, Variants;
type
MyThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
public
i1 : integer;
s1 : string;
end;
implementation
procedure MyThread.Execute;
begin
s1:= IntToStr(i1);
end;
end
Соответственно в главной проге
Procedure TMainForm.ReadData(Sender: TObject);
Var iz : integer;
begin
Thread := MyThread.Create(true);
Thread.Priority := tpLowest;
Thread.FreeOnTerminate:= false;
For iz:= 0 to 9 do BEGIN
Thread.i1:= iz;
Thread.Resume;
Labels[iz].Caption:= Thread.s1;
END;
Thread.Destroy;
end;
Чтоже у меня не правильно? Пробовал разные варианты. И Create/Destroy ставил в тело цикла. И Create(false) писал - ничего. "s1" всегда = "";
← →
Digitman © (2006-01-19 09:59) [8]MyThread = class(TThread)
private
FParameter: Integer;
FResult: String;
protected
procedure Execute; override;
public
constructor Create(Parameter: Integer);
property MyResult: String read FResult;
end;
..
constructor MyThread.Create(Parameter: Integer);
begin
FParameter: Integer;
inherited Create(False);
end;
procedure MyThread.Execute;
begin
FResult := IntToStr(FParameter);
end;
...
Procedure TMainForm.ReadData(Sender: TObject);
Var iz : integer;
begin
For iz:= 0 to 9 do BEGIN
Thread := MyThread.Create(iz);
Thread.WaitFor;
Labels[iz].Caption:= Thread.ExecResult;
Thread.Destroy;
END;
end;
← →
EarlVadim © (2006-01-19 10:04) [9]Digitman © (19.01.06 09:59) [8]
Спасибо... Ведь как все просто, но сам не допрёшь...
← →
EarlVadim © (2006-01-20 10:15) [10]Digitman © (19.01.06 09:59) [8]
Этсамое.... Ну прикрутил я всё в свою прогу, все работает, но эффекта положительного нет. Все дело в Thread.WaitFor;
Обясняю: в теле MainForm организован цикл "по всем хостам читать файл и данные файла хитрым образом обрабатывать". Чтение файлов я воткнул в поток, а вот обработка у меня в MainForm. Так вот, пока файл по сети читаясь тупит, программа, следуя WaitFor тоже приморожена. Но если WaitFor убрать, то естессно нифига неработает. Какой для этого выход?
Мне видится так, что я должен в поток переместить всю процедуру (и чтения файлов и их обработку и вывод результатов в окно программы).
Или есть другие способы?
← →
Digitman © (2006-01-20 13:45) [11]
> Или есть другие способы?
Конечно же есть.
WaitFor был простейшим решением для оригинального примера в [7].
В изложенном же в [10] случае можно поступить, например, так:
MyThread = class(TThread)
private
FParameter: String;
FResult: String;
protected
procedure Execute; override;
public
constructor Create(DownloadURL: String; Suspended: Boolean);
property DownloadedFileName: String read FResult;
end;
..
constructor MyThread.Create(DownloadURL: String; Suspended: Boolean);
begin
FParameter := DownloadURL;
inherited Create(Suspended);
end;
procedure MyThread.Execute;
begin
try
.. попытка закачки файла по локатору в FParameter ..
FResult := ... полный путь к закачанному файлу .. //закачка успешна
except
.. закачка неуспешна ..
end;
end;
...
Procedure TMainForm.ProcessFile(Sender: TObject);
begin
with TMyThread(Sender) do begin
if DownloadedFileName <> "" then //закачка успешна
Labels[..].Caption:= DownloadedFileName;// эмуляция "обработки файла" для примера
end;
end;
Thread := MyThread.Create(... конкретный URL файла .., True);
Thread.FreeOnTerminate := True;
Thread.OnTerminate := ProcessFile; //назначенный обработчик события OnTerminate будет вызван в осн.потоке процесса сразу после завершения метода MyThread.Execute;
// Labels[iz].Caption:= Thread.ExecResult;
// Thread.Destroy;
← →
EarlVadim © (2006-01-24 17:12) [12]Digitman © (20.01.06 13:45) [11]
Нифига не работает такой вариант.
ProcessFile - не выполняется.
← →
Digitman © (2006-01-24 17:26) [13]
> EarlVadim © (24.01.06 17:12) [12]
отладчик тебе в руки
← →
EarlVadim © (2006-01-24 17:47) [14]Digitman © (24.01.06 17:26) [13]
По шагам прошёл, не передаётся выполнение туда и всё тут.
В общем, в печали я.
← →
EarlVadim © (2006-01-24 18:23) [15]Скажу больше, без Thread.WaitFor; Execute вообще не факт что получит управление. У меня в 4-х вариантах пробы потока, так вот работают потоки в 2-х, а в 2-х других - НЕТ. Все вызовы идентичны, голова уже пухнет...
← →
Digitman © (2006-01-25 09:38) [16]
> без Thread.WaitFor; Execute вообще не факт что получит управление
получит, если поток не создан как suspended.
и это факт.
установи брейкпойнт
procedure MyThread.Execute;
begin //<- ЗДЕСЬ
..
end;
вызови
Thread := MyThread.Create(..., False);
и убедись в "факте".
Единственное что я упустил в [11] и что важно в том контексте :
//поток создается как suspended для того чтобы перед началом его выполнения можно было назначить нужные нам св-ва
Thread := MyThread.Create(... конкретный URL файла .., True);
//поток должен разрушить себя сам после завершения выполнения
Thread.FreeOnTerminate := True;
//назначение обработчика события
Thread.OnTerminate := ProcessFile; //назначенный обработчик события OnTerminate будет вызван в осн.потоке процесса сразу после завершения метода MyThread.Execute;
//и наконец "пробуждаем" поток !
Thread.Resume;
← →
EarlVadim © (2006-01-25 11:20) [17]Thread.Resume; я пробовал и раньше. Сейчас сделал все как написано.
Проставил два брейка.
Результат:
Ни на BEGIN в Execute, ни на BEGIN в ProcessFile выполнение не останавливается, -> ход выполнения до них не добирается.
Может D7 глючит? Хотя в другой версии той же проги у меня работает код без WaitFor. Но та версия сама по себе меня не устраивает.
Придётся с нуля проект создавать, видимо.
← →
Digitman © (2006-01-25 11:28) [18]
> Может D7 глючит?
"Это вряд ли .." (с) Ф.Сухов
Приводи полный код в последней своей редакции (неработающей)
← →
EarlVadim © (2006-01-25 12:45) [19]
> Приводи полный код в последней своей редакции (неработающей)
:) Мне уже подсказали. Все дело в неверном месте вызова DESTROY;
Поток не устевает выполнится до EXECUTE когда основная программа вызывает DESTROY. Щас переписал код так, чтобы поток жил постоянно.
Просто пока параметром не пошлю TRUE - в теле EXECUTE выполняется пустой цикл. После выполнения EXECUTE флаг снова ставлю в FALSE и выполняю опять пустой цикл.
Вот про красоту решения спорить не готов. Нормально это или нет?
Но тебе отдельное спасибо за помощь в понимании основного принципа потоков и терпение к нам, "чайникам".
← →
EarlVadim © (2006-01-25 12:53) [20]
> Digitman © (25.01.06 11:28) [18]
И кстати, вопрос о том, как быстро опросить заданные хосты, на предмет их наличия в сети в данный момент. Я сейчас использую:
For i:= 0 to Len-1 do begin
st:= TStringList.Create;
try
If FileExists(PChar(CF[i])+"\unitinfo.txt") then begin // в CF:array of string - хранятся пути в виде "\\host1\path1"
st.LoadFromFile(PChar(CF[i])+"\unitinfo.txt");
FOutPar[i]:= ST.Strings[4];
// это вывод результата тестовый - просто возвращается 4-я строка файла.
// На самом деле будет работа с данными файла намного насыщеннее
end else FOutPar[i]:= "";
finally
FReady[i]:= true;
// Установка флага о том, что этот сервер прочитан, основная прога
// Ориентируется на него и забирает FOutPar не дожидаясь остальных
st.free;
end;
end;
Есть советы для меня?
← →
Digitman © (2006-01-25 13:14) [21]
> про красоту решения спорить не готов. Нормально это или
> нет?
Нет, не нормально.
Пустой цикл вида while Flag=False do; ощутимо отнимает процессорное время у других процессов и у других потоков того же процесса, не делая ничего полезного.
Да и нужно ли такое решение с циклами ?
Сомнительно.
Для начала следует одназначно определиться, будет ли созданный тобой поток использоваться однократно (т.е. будучи единожды созданный выполнит свою "миссию", после чего должен навсегда "умереть") либо многократно (т.е. будучи единожды созданный сможет выполнить множество "миссий", определенных различными передаваемыми ему параметрами)
Цикл в теле Execute (неважно какой, об этом - позже) напрашивается лишь при многократном использовании одного и того же объекта-потока.
← →
EarlVadim © (2006-01-25 13:54) [22]
> либо многократно
Да. Через определяемые в настройках программы промежутки времени.
Приоритет потока Thread.Priority := tpLowest;
А цикл в потоке у меня выглядит так
While true do
If FCheck then BEGIN
FCheck:= false;
For i:= 0 to Lens-1 do FReady[i]:= false;
For i:= 0 to Lens-1 do begin
st:= TStringList.Create;
try
If FileExists(PChar(CF[i])+"\unitinfo.txt") then begin
st.LoadFromFile(PChar(CF[i])+"\unitinfo.txt");
FOutPar[i]:= ST.Strings[4];
end else FOutPar[i]:= "";
finally
FReady[i]:= true;
end;
st.free;
end;
END;
← →
Digitman © (2006-01-25 14:04) [23]А должен выглядеть хотя бы так :
while not Terminated do begin
FCommand := WaitCommand;
if Terminated then Break;
FResults := ProcessCommand(FCommand);
end;
← →
Digitman © (2006-01-25 14:05) [24]А должен выглядеть хотя бы так :
while not Terminated do begin
FCommand := WaitCommand;
if Terminated then Break;
FResults := ProcessCommand(FCommand);
end;
← →
EarlVadim © (2006-01-25 14:22) [25]FCommand и FResults
Это переменные объявленные в private?
а ProcessCommand это процедура с моим кодом?
А что и откуда WaitCommand? какие значения может возвращать?
← →
Digitman © (2006-01-25 15:15) [26]
> EarlVadim © (25.01.06 14:22) [25]
> FCommand и FResults
> Это переменные объявленные в private?
предположим - да...
> ProcessCommand это процедура с моим кодом?
опять же - например... т.е. - да ...
> что и откуда WaitCommand? какие значения может возвращать?
Это некий метод твоего класса TMyThread, который "приостанавливает" твой поток для ожидания "команд", ему адресованых ...
Он может возвращать некую команду, "посланную" потоку "извне" и которую данный поток должен исполнить.
← →
EarlVadim © (2006-01-25 15:31) [27]Теперь понятно.
Ну это симпатичный код.
Только вопрос с ожиданием - он открыт.
Хотя можно попробовать SUSPEND и RESUME передавать.
Но меня сейчас напрягает больше вопрос именно касающийся сетей, описанный мною выше. (EarlVadim © (25.01.06 12:53) [20]).
← →
Digitman © (2006-01-25 15:43) [28]
> меня сейчас напрягает больше вопрос именно касающийся сетей,
> описанный мною выше
Дай свое определение термину "опрос хоста" ..
← →
EarlVadim © (2006-01-25 16:35) [29]
> Дай свое определение термину "опрос хоста" ..
Наверное это PING.
← →
Digitman © (2006-01-25 16:50) [30]т.е. тебе необходимо осуществить Packet INternet Gopher ...
← →
EarlVadim © (2006-01-25 18:01) [31]
> т.е. тебе необходимо осуществить Packet INternet Gopher
> ...
Не уверен.
Мне надо передать некой функции имя хоста ( напр. \\SERVER ) и в качестве результата получить от неё TRUE - если SERVER в локальной сети присутствует.
Второй момент, есть ли способы ускорить первоначальное обращение к файлам на удалённом компьютере. Дело в том, что если программа читает эти файлы с некоторой периодичностью, напр. раз в минуту, то первое чтение занимает существенно бОльшее время чем последующие. Не знаю причин этого, и возможно на это нельзя повлиять. Поэтому и спрашиваю.
← →
Digitman © (2006-01-26 08:35) [32]Хосты в ЛВС имеют IP-адреса ?
← →
EarlVadim © (2006-01-26 08:55) [33]
> Хосты в ЛВС имеют IP-адреса ?
Имеют. Сеть MS на TCP/IP.
← →
Digitman © (2006-01-26 09:30) [34]Самый простой способ - использовать ICMP-клиента.
Например , компонент TIdICMPClient в составе Indy-пакета
← →
EarlVadim © (2006-01-26 10:11) [35]Делаю так:
IdIcmpClient1.ReceiveTimeout:= 10000;
IdIcmpClient1.Port:= 80;
IdIcmpClient1.Host:= Edit1.Text;
IdIcmpClient1.Ping("",0);
Label1.Caption:= IdIcmpClient1.ReplyStatus.FromIpAddress;
Некоторые хосты возвращают IP-адрес, а некоторые говорят
Error #11001 Host not found. Задолго то окончания TimeOut;
← →
Digitman © (2006-01-26 10:40) [36]Это проблема с разрешением имени в IP-адрес - либо имя хоста неверно указано, либо на DNS-сервере отсутствует запись о хосте с указанным именем, либо проблемы в самой DNS-подсистеме
← →
EarlVadim © (2006-01-26 10:46) [37]Да действительно, не PINGуется этот хост.
М-да FileExists о таких мелочах не волнуется.
Страницы: 1 вся ветка
Форум: "Сети";
Текущий архив: 2006.05.21;
Скачать: [xml.tar.bz2];
Память: 0.56 MB
Время: 0.014 c