Форум: "Основная";
Текущий архив: 2003.03.13;
Скачать: [xml.tar.bz2];
ВнизОбъясните мне про потоки и защищенность их методов! Найти похожие ветки
← →
Aleksandr (2003-03-03 10:21) [0]Ну не могу я понять... Есть в программе штук семьдесят экземпляров одного и того же объекта-наследника TThread - отличается каждый тем, что работает с индивидуальным файлом, указанным при инициализации. Методы этих потоков - чтение/запись в эти файлы - приватные, доступен другим только один метод - помещение данных в эти потоки. Часть данных программы куда-то пропадала, я сделал логирование - все время проскакивает error I/O 32 или 5. В общем, получается, что в один метод в одно время кто-то обращается дважды. Объясните мне, глупому, может поток дважды одновременно войти в свой метод, или в каких истчо случаях может произойти "накладка" одновременного входа в один метод?
← →
Digitman (2003-03-03 10:26) [1]ты хоть бы фрагмент кода привел
← →
Aleksandr (2003-03-03 12:30) [2]2 Digitman c : Ох, ну если Вы настаиваете... Поймите правильно - кода там - многие тысячи строчек, поэтому, спрашивая о причинах проблемы, я, соответственно, не знаю, какую часть приводить. Ну, вот метод, где, как я предполагаю, все кроется:
procedure TRITableItem.CloseUpdateFile;
var
Ev : TRiEvent; //простой объект из полей команда/строка
AD : longint;
LastTimeOfs : longint;
S : TDosFileStream; //наследник файл-стрима, отличается тем, что в начале пишется идентификатор класса, "умеющего" читать данный экземпляр файла.
ReloadIndex : boolean;
begin
ReloadIndex:=false;
AD:=MasterTbl.GetTableTime(FIDT); //получить время последней метки времени из внешней коллекции
if NOT FileExists(UpdIndexfName) then
MakeIndexWithOutOpen; //, приватный метод, пересоздать индексный файл
LastTimeOfs:=IndexList.GetOffsetByLastTime(FIDT,FLastUpdWriteTime); //получить смещение от предыдущего изменения из внешней коллекции
if (LastTimeOfs>0) AND Assigned(FUpdFile) then begin //если открыт файл Handle FUpdFile
S:=TDosFileStream.Create(TmpDir+ChangeFileExt(ExtractFileName(UpdateFName),".tmp"),fmCreate);//создать временный файл
FUpdFile.Seek(0,soFromBeginning); //сместиться в файле апдейтов на начало
S.CopyFrom(FUpdFile,LastTimeOfs); //копировать из файла апдейтов во временный по смещение
while FSaveEvents.Count>0 do begin //пока количество изменений больше 0
S.Seek(0,soFromEnd); //отпозиционироваться на конец временного файла
try
Ev:=TRiEvent(FSaveEvents.Items[0]);
if Assigned(Ev) AND (Ev.Command=revMarkTime) then begin //если отметка времени
AddToIndexFile(Ev.GetDateTime,S.Position); //приватный метод, в индексный файл впихнуть позицию
ReloadIndex:=true
end;
S.Put(Ev); //записать во временный файл апдейт
Ev.Free
except
on E:Exception do
LogDBError(DBItemLogIDChar, "Close update file",E.Message+" " + TableName)
end;
FSaveEvents.Delete(0) //удалить из коллекции
end;
FUpdFile.Seek(LastTimeOfs, soFromBeginning); //сместиться в файле апдейтов на последнее изменение
S.Seek(0,soFromEnd); //сместиться на конец временного файла
while FUpdFile.Position<FUpdFile.Size do begin //от текущего до конца файла апдейтов
Ev:=TRiEvent(FUpdFile.Get);
S.Put(Ev); //записать во временный файл апдейт
S.Seek(0,soFromEnd); //сместиться на конец
Ev.Free
end;
S.Free; //закрыть временный
FreeAndNil(FUpdFile); //закрыть файл апдейтов
DeleteFile(PChar(UpdateFName)); //похерить файл апдейтов
MoveFile(PChar(TmpDir+ChangeFileExt(ExtractFileName(UpdateFName),".tmp")),PChar(UpdateFName));
//перенести временный как файл апдейтов
SetFileTimeStamp(UpdateFName,AD); //поставить время
SetFileTimeStamp(UpdIndexFName,AD) //поставить время
end;
FLastUpdWriteTime:=AD
end;
← →
Digitman (2003-03-03 12:48) [3]ну, судя по тому, что ты получаешь отказы
ERROR_ACCESS_DENIED = 5
ERROR_SHARING_VIOLATION = 32
то, действительно, где-то в каком-то код.потоке осуществляется попытка обращения к файлу (возможно, открытому уже другим потоком с определ. атрибутами/привелегиями доступа) без соответствующих на то прав/атрибутов/привелегий
в этом ли фрагменте происходит "беда" или в ином, и в какой момент, при каких условиях - сложно сказать.
нужен как минимум еще код методов конструктора/деструктора потока и код метода Execute()
кр.того оч желательна хотя бы лог.схема, по которой при каких-то условиях вызываются конструкторы/деструкторы объектов-потоков
← →
Dms (2003-03-03 13:00) [4]Попробуй использовать методы синхронизации для доступа к разделяемым ресурсам (файлам). Например, бинарные семафоры. Ошибка скорее всего из-за того, что происходит обращение к уже используемуму файлу.
← →
Aleksandr (2003-03-03 13:56) [5]Констракторы вызываются при запуске программы, дестракторы - при завершении. Потоки существуют в течение всего существования программы. Их Execute:
begin
while not Terminated do begin
while HaveWork and not Terminated do //если есть в коллекции объекты для обработки (просто возвращает Count>0
DoWork; // выполнить процедуру
if not Terminated then
Suspend
end;
DoWork включает в себя 3 метода: открытие файла, запись событий во временную коллекцию (FSaveEvents) и закрытие c сохранением (приведенный мной код). Есть доступный для других потоков метод, добавляющий объекты на обработку в коллекцию:
procedure TRITableItem.AddPutData( EvList : TCollectionList);
begin
EnterCriticalSection(FPutCS); //критическая секция, являющаяся внутренним свойством этого потока
try
while EvList.Count <> 0 do try
FPutQueue.Add( EvList.Items[0]) //в свою коллекцию вставить
finally
EvList.Delete(0)
end
finally
LeaveCriticalSection(FPutCS)
end
end;
Вот, собственно, и все... Потому у меня и возникают дикие мысли, что один поток дважды может сам в себя входить... Вариант, что два экземпляра этого потока работают с одним файлом, просто исключен.
← →
Digitman (2003-03-03 14:27) [6]а крит.секция зачем нужна здесь ? поясни
для чего каждому потоку своя собственная крит.секция ? если ты таким образом пытаешься защитить какой-то ресурс от одновременного мультипоточного доступа, то секция д.б. глобально доступна для всех потоков, и уж никак не св-вом объекта-потока она должна быть !
← →
SVM (Perm) (2003-03-03 14:27) [7]А к файлу FUpdFile доступ осуществляется только в контексте одного потока?
← →
REA (2003-03-03 14:45) [8]Не знаю насколько совет в тему, но еще в Delphi есть фенечка TMutiReadExclusiveWriteSynchronizer, если надо общую память читать многими потоками, а писать одним.
← →
Polevi (2003-03-03 14:54) [9]я бы Execute реализовал примерно так
while not Terminated do
begin
WaitForMultipleObjects(.. //ждем файл для обработки или признак завершения потока (needStopEvent,processFileSem)
if needStopEvent работу then break
else
begin
Извлекаем файл для обрабоки из списка
DoWork
end;
end
в основном потоке
processFileSem:=CreateSemaphore(nil,0,100,nil);
needStopEvent:=CreateEvent(nil,true,false,nil);
procedure AddFileToProcess(AFileName:string)
begin
AddFileToList(AFileName); //Добавляем файл для обработки в список
ReleaseSemaphore //первый свободный поток начнет обработку файла
end;
← →
Polevi (2003-03-03 14:59) [10]PS
лучше так
procedure AddFileToProcess(AFileName:string);
begin
EnterCriticalSection
try
FFileList.Add(AFileName);
ReleaseSemaphore //первый свободный поток начнет обработку файла
finally
LeaveCriticalSection;
end;
← →
REA (2003-03-03 15:02) [11]2Polevi:
может быть полезно
TThreadList represents a thread-safe list
← →
Polevi (2003-03-03 15:11) [12]2REA © (03.03.03 15:02)
спасибо, я в курсе
в данном случае код с Enter, Leave нагляднее, IMHO
← →
Aleksandr (2003-03-03 18:54) [13]Мда... сорри, что долго не подключался. Вижу, что много можно бы сделать иначе. Только по вопросу о критических секциях - если сделать глобальную критическую секцию, а потом использовать ее в для защиты метода потока - не получится ли, что 69 экземпляров потока будут ждать, пока 1 пройдет через этот код, при всем при том, что каждый экземпляр юзает сугубо свой файл?
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2003.03.13;
Скачать: [xml.tar.bz2];
Память: 0.49 MB
Время: 0.007 c