Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.04.01;
Скачать: [xml.tar.bz2];




Вниз

Как защитить переменные в потоке? 


Aleksandr   (2002-03-21 14:49) [0]

Уважаемые коллеги!
Может, я чего-то с методом тыка не понимаю...

Внутри Execute потока у меня есть переменная типа TMemoryStream, которая загружает информацию и передается в качестве параметра в создаваемый там же другой поток:

M:=TMemoryStream.Create;
Thread.Connection.ReceiveStream(M); //Thread - это переменная, которой и принадлежит Execute
with TUpdateProcessor.CreateOnStream(true,M) do begin //истчо один поток
FreeOnTerminate:=false;
UpdateType:=utRemoteUpdate;
Resume;
WaitFor;
UpdateResult:=UpdateType;
Free
end;

Суть проблемы в том, что потоков, выполняющих этот код, может быть несколько, и, хотя М является локальной переменной, при трассировке программы она после передачи в TUpdateProcessor уже в его теле принимает значения, которые считывает из сокета другой экземпляр Thread. Другой пример: в этом же коде все время ведется лог в текстовый файл, открытый fmOpenReadWrite or fmShareDenyNone. В этот лог разные экземпляры потока тоже одновременно рисуют, то есть вместо
14:40:10: Обработка обновлений 0001...
14:40:11: Обработка 0001 успешно завершена
14:40:12: Обработка обновлений 0002...
14:40:13: Обработка 0002 успешно завершена
пишется
14:40:10: Обработка обновлений 0001...
14:40:11: Обработка обновлений 0002...
14:40:12: Обработка 0001 успешно завершена
Попробовал я засунуть код в рамки CriticalSection, дык он тогда вообще просто умирает. В чем тут смысл жизни?



Виктор Щербаков   (2002-03-21 15:01) [1]

threadvar



Aleksandr   (2002-03-21 15:14) [2]

Низя threadvar внутри метода объявлять :(



Виктор Щербаков   (2002-03-21 15:20) [3]

А зачем внутри метода?



Digitman   (2002-03-21 15:21) [4]

не понял - приведенный тобой код предст.собой фрагмент тела TThread.Execute, так что ли ?



Aleksandr   (2002-03-21 15:27) [5]

Мда, и в самом деле...
Ну, вот например... Когда файл лога зашкаливает за размер, его надо убить и пересоздать. Файл лога - это приватное поле потока типа TFileStream:
if FLogFile.Size>MaxLogFileSize then begin
FLogFile.Free;
FLogFile:=TFileStream.Create(FLogFileName,fmCreate);
FLogFile.Free;
FLogFile:=TFileStream.Create(FLogFileName,fmOpenReadWrite or fmShareDenyNone)
end;
Проверка на размер внутри метода добавления записей в лог. Но у меня этот код не срабатывает - файл застывает на данном размере и больше не двигается... Очевидно, происходит исключительная ситуация с тем, что пока выполняется этот код, поток успевает опять вызвать добавление записи в лог... И как тут запретить ему дважды входить в метод?



Aleksandr   (2002-03-21 15:28) [6]

2 Digitman - Да



Виктор Щербаков   (2002-03-21 15:32) [7]


> И как тут запретить ему дважды входить в метод?

С помощью критических секций или объектов ядра.



Aleksandr   (2002-03-21 15:55) [8]

Что-то я тогда совсем не понимаю, когда можно использовать критические секции, а когда - нет... Ну неграмотный я, и во всяких Делфи для профессионалов эта тема весьма слегка описана...



Digitman   (2002-03-21 15:58) [9]

>Aleksandr

т.е., полностью это выглядит вот так ?


procedure TSomeThread.Execute;
var
M: TMemoryStream;
begin
M:=TMemoryStream.Create;
Thread.Connection.ReceiveStream(M); // Thread = Self ? И зачем это ?
with TUpdateProcessor.CreateOnStream(true,M) do begin
FreeOnTerminate:=false;
UpdateType:=utRemoteUpdate;
Resume;
WaitFor;
UpdateResult:=UpdateType;
Free
end;
end;


если - так (а с твоих слов - именно так !), то о какой вообще синхронизации может идти речь ? Ну, создал ты MemoryStream, передал его параметром в другой поток - так ведь ждешь все равно, пока другой поток не завершится ? А завершился - продолжай работать с MemoryStream, ничто этому не мешает. Кстати, и не уничтожаешь ты MemoryStream нигде - если это не делается в TUpdateProcessor, утечка памяти тебе гарантирована.



Aleksandr   (2002-03-21 16:08) [10]

Нет, полный код, конечно, не такой... Thread - это не Self, это параметр Execute для IdTCPServer - его метод присваивается создаваемому потоку каждого клиентского соединения... А память освобождается внутри апдэйтора... А что с синхронизацией?



Aleksandr   (2002-03-21 16:21) [11]

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



Digitman   (2002-03-21 16:25) [12]

какой еще "параметр" ? Нет у TThread.Execute параметров ! Выражайся точнее



Aleksandr   (2002-03-21 16:46) [13]

Заранее извиняюся за спам...

unit Webthreads;
interface
type
TWebDataProcessor=class (TThread)
private
FSleepInterval : word;
FDir : string;
FClient : TIdTCPClient;
FBadDir : string;
FLogFile : TFileStream;
FLogFileName : string;
public
constructor Create(ASuspensed : boolean; ASleepInterval : longint;ADir : string; AHost : string; APort : word);
procedure Execute; override;
destructor Destroy; override;
function SendFile(AFileName : string) : boolean;
procedure AddLog(Msg : string);
published
property BadDir : string read FBadDir write FBadDir;
end;

TRsWebServer=class (TIdTCPServer)
private
FBadDir : string;
FLogFile : TFileStream;
FLogFileName : string;
procedure FOnExecute(AThread: TIdPeerThread);
public
constructor CreateOnPort(APort : word);
destructor Destroy; override;
procedure AddLog(Msg : string);
published
property BadDir : string read FBadDir write FBadDir;
end;

const
RSFileName="$FileNameOK";
RSFileSize="$FileSizeOK";
RSFileData="$FileDataOK";
RSUpdError="$UpdatError";
WebSrvLogFileName = "websrv.log";
WebStmLogFileName = "webstrm.log";

var
WebSection1 : TRTLCriticalSection;
FileSection1 : TRTLCriticalSection;
FileSection2 : TRTLCriticalSection;

implementation

constructor TRsWebServer.CreateOnPort(APort: word);
begin
Inherited Create(nil);
DefaultPort:=APort;
FLogFileName:=CorrectPath(LogFilesDir)+WebSrvLogFileName;
if FileExists(FLogFileName) then begin
FLogFile:=TFileStream.Create(FLogFileName,fmOpenReadWrite or fmShareDenyNone);
if FLogFile.Size>30000 then begin
FLogFile.Free;
FLogFile:=TFileStream.Create(FLogFileName,fmCreate);
end;
FLogFile.Free
end
else begin
FLogFile:=TFileStream.Create(FLogFileName,fmCreate);
FLogFile.Free
end;
FLogFile:=TFileStream.Create(FLogFileName,fmOpenReadWrite or fmShareDenyNone);
FLogFile.Seek(0,soFromEnd);
InitializeCriticalSection(FileSection2);
OnExecute:=FOnExecute
end;

procedure TRsWebServer.AddLog(Msg: string);
var
s : string;
P : Pointer;
begin
if NOT Assigned(FLogFile) then
Exit;
try
EnterCriticalSection(FileSection2);
if FLogFile.Size>30000 then begin
FlogFile.Free;
FLogFile:=TFileStream.Create(FLogFileName,fmCreate);
FLogFile.Free;
FLogFile:=TFileStream.Create(FLogFileName,fmOpenReadWrite or fmShareDenyNone)
end;
S:=DateTimeToStr(Now)+": "+Msg+#13#10;
P:=Pointer(s);
FLogFile.WriteBuffer(P^,length(S))
finally
LeaveCriticalSection(FileSection2)
end
end;

procedure TRsWebServer.FOnExecute(AThread: TIdPeerThread);
var
aFileName : string;
AuID : integer;
aFileSize : integer;
M : TDosMemoryStream;
s : string;
UType : TUpdateType;
begin
try
try
M:=TDosMemoryStream.Create;
AFileName:=AThread.Connection.Readln;
AThread.Connection.Writeln(rsFileName);
aFileSize:=StrToIntDef(AThread.Connection.ReadLn,-1);
AThread.Connection.Writeln(rsFileSize);
AThread.Connection.ReadStream(M,AFileSize);
AThread.Connection.Writeln(RSFileData);
M.Seek(0,soFromBeginning);
if M.Size>0 then begin
s:=ExtractFileName(AFileName);
s:=System.Copy(S,1,Pos(".",S)-1);
aUID:=StrToInt("$"+S);
AddLog("



Aleksandr   (2002-03-21 16:47) [14]

Продолжение:


{ TWebDataProcessor }
function TWebDataProcessor.SendFile(AFileName: string): boolean;
var
R : TRiHeader;
F : TFileStream;
s : string;
begin
Result:=false;
R:=nil;
try
R:=TRIHeader(GetFromFileStream(AFileName));
F:=nil;
if FileExists(R.TransName) then begin
FClient.Writeln(ExtractFileName(R.TransName));
s:=FClient.ReadLn;
if s=rsFileName then try
F:=TFileStream.Create(R.TransName,fmOpenRead OR fmShareDenyNone);
FClient.Writeln(IntToStr(F.Size));
s:=FClient.Readln;
if s=rsFileSize then begin
FClient.WriteStream(F);
s:=FClient.ReadLn;
if s=rsFileData then
Result:=true
else
AddLog("Error file data ok - "+R.TransName)
end
else
AddLog("Error file size ok - "+R.TransName)
finally
F.Free
end
else
AddLog("Error file name ok - "+R.TransName)
end
else
AddLog("File Not found - "+R.TransName)
finally
if Result then begin
DeleteFile(R.TransName);
DeleteFile(AFileName)
end
else if FBadDir<>"" then begin
DeleteFile(AFileName);
{$IFNDEF Remote}
MoveFileWithUnicName(R.TransName,FBadDir+ExtractFileName(R.TransName))
{$ENDIF}
end;
R.Free
end
end;

constructor TWebDataProcessor.Create;
begin
inherited Create(true);
FreeOnTerminate:=false;
FSleepInterval:=ASleepInterval;
FDir:=ADir;
FClient:=TIdTCPClient.Create(nil);
FClient.Port:=APort;
FClient.Host:=AHost;
FLogFileName:=CorrectPath(LogFilesDir)+WebStmLogFileName;
if FileExists(FLogFileName) then begin
FLogFile:=TFileStream.Create(FLogFileName,fmOpenReadWrite or fmShareDenyNone);
if FLogFile.Size>30000 then begin
FLogFile.Free;
FLogFile:=TFileStream.Create(FLogFileName,fmCreate);
end;
FLogFile.Free
end
else begin
FLogFile:=TFileStream.Create(FLogFileName,fmCreate);
FLogFile.Free
end;
FLogFile:=TFileStream.Create(FLogFileName,fmOpenReadWrite or fmShareDenyNone);
FLogFile.Seek(0,soFromEnd);
InitializeCriticalSection(WebSection1);
InitializeCriticalSection(FileSection1);
if NOT ASuspensed then
Resume
end;

procedure TWebDataProcessor.AddLog(Msg: string);
var
s : string;
P : Pointer;
begin
if NOT Assigned(FLogFile) then
Exit;
try
EnterCriticalSection(FileSection1);
if FLogFile.Size>30000 then begin
FlogFile.Free;
FLogFile:=TFileStream.Create(FLogFileName,fmCreate);
FLogFile.Free;
FLogFile:=TFileStream.Create(FLogFileName,fmOpenReadWrite or fmShareDenyNone)
end;
S:=DateTimeToStr(Now)+": "+Msg+#13#10;
P:=Pointer(s);
FLogFile.WriteBuffer(P^,length(S))
finally
LeaveCriticalSection(FileSection1)
end
end;

procedure TWebDataProcessor.Execute;
var
HeadersList : TList;
FData : PWin32FindData;
i : integer;
s : string;
begin
Sleep(0);
try
Repeat
HeadersList:=TList.Create;
if SearchFiles32(HeadersList,FDir+HeadersMask) then try
AddLog("Found file(s) to send - "+IntToStr(HeadersList.Count));
EnterCriticalSection(WebSection1);
for i:=0 to HeadersList.Count-1 do try
FData:=HeadersList.Items[i];
if NOT FClient.Connected then
FClient.Connect;
AddLog("Connected to remote...");
if FClient.Connected AND FileExists(CorrectPath(FDir)+ExtractFileName(FData.cFileName)) then begin
s:=CorrectPath(FDir)+ExtractFileName(FData.cFileName);
if SendFile(CorrectPath(FDir)+ExtractFileName(FData.cFileName)) then
AddLog("File sending successfully - "+s)
else
AddLog("File cannot sending - "+s)
end
else if NOT FClient.Connected then
AddLog("Connection unavailable");
if FClient.Connected then begin
AddLog("Disconnected to remote.");
FClient.DisConnect
end;
Dispose(FData);
finally
end
finally
LeaveCriticalSection(WebSection1);
end;
while HeadersList.Count>0 do
HeadersList.Delete(0);
HeadersList.Free;
Sleep(FSleepInterval);
until Terminated
except
on E:Exception do
AddLog("Exception in execute - "+E.Message)
end
end;

destructor TWebDataProcessor.Destroy;
begin
if Assigned(FClient) then
FClient.Free;
DeleteCriticalSection(WebSection1);
DeleteCriticalSection(FileSection1);
inherited
end;

end.



Digitman   (2002-03-21 17:25) [15]

А чито это меняет ? M - локальная переменная в процедуре обработки события. До тех пор, пока не отработает TDBUpdateProcessor.Execute, никто и ничто больше, судя по коду, не обращается к M. Для чего ж синхронизация-то доступа к M ?



Aleksandr   (2002-03-21 17:40) [16]

Тем не менее, получается, что он же и обращается к ней либо не ждет завершения DBUpdateProcessor+потока...
Например, если я сделаю в конце Execute освобождение M.Free, то в Execute DBUpdateProcessora будет Access Violation - там Stream окажется никакой. Хотя уничтожение должно произойти только после отработки :(



Digitman   (2002-03-21 17:49) [17]

так это ж - совсем иная проблема !
приводи код класса TDBUpdateProcessor - там посмотрим, чего он там "висит" (так что дождаться его завершения невозможно по WaitFor)



Aleksandr   (2002-03-21 18:05) [18]

Ох... там кода и на пять отправок не полезет... У меня главная проблема в том, что вот в приведенном коде есть три проблемы:
1) когда файл лога перескакивает предел, винды убивают программу.
2) программа очень быстро съедает все ресурсы процессора.
3) все время растет память программы.
Проблемы - в этом модуле, если его выключить из основной, то все становится ок.



Digitman   (2002-03-21 18:35) [19]

Нет, ну а какое это отношение к синхронизации доступа к M имеет ? Ты ж ведь так именно вопрос поставил ? Давай уж с этим разберемся, а уж потом логом займемся ....

Еще раз :

M - локальна в вызывающей процедуре, содержит ссылку на объект.
Ты передаешь значение M параметром в конструктор другого объекта-потока, и, пока этот "другой" поток не завершится, ты нигде к M не обращаешься в вызывающем коде. В вызванном потоке, я предполагаю, идет обращение к объекту M без его уничтожения в финале. О завершении же созданного "другого" потока тебе сообщит успешное завершение вызова метода WaitFor. Он (WaitFor) у тебя успешно завершается ??




Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.04.01;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.78 MB
Время: 0.05 c
1-199             vlad451               2002-03-20 10:30  2002.04.01  
DLL


6-268             Дремучий              2002-01-16 11:10  2002.04.01  
с чем едят тип TRASConn?


3-55              kaif                  2002-03-07 14:19  2002.04.01  
можно ли обрезать строку без использования UDF?


1-155             SB.John               2002-03-21 12:47  2002.04.01  
как узнать сколько памяти занимает какой-либо объект?


3-68              anp                   2002-03-07 15:45  2002.04.01  
Выполнение SQL-запроса