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


Автор: dma 29.2.2008, 21:54
Господа, помогите пожалуйста решить небольшой вопрос.
Заключается он в следующем:

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

  repeat
    IsAllowed := true;
    try
      Sleep(10);
      if FileExists(FileName) then
        FFile := TFileStream.Create(FileName, fmOpenRead)
      else
      begin
        IsAllowed := False;
        break;
      end;
    except
      IsAllowed := False;
    end;
  until IsAllowed;

Таким образом получается, что когда файл скопируется, то какой-то из потоков его откроет на чтение, а остальные дальше будут ожидать освобождения файла, т.к. не смогут получить к нему доступ. Это хорошо smile Но после того, как поток произвёл некоторые действия с этим файлом, он должен его удалить. Чтобы его удалить, нада сначала закрыть FileStream. И вот здесь возникает проблема! Как гарантированно успеть удалить файл до того, как его успеет схватить другой поток после закрытия FileStream'a?

Буду благодарен за любую помощь. Или за предложение лучшего варианта решения проблемы smile

Автор: bems 29.2.2008, 23:17
Вместо TFileStream используй THandleStream. Файл открывай апишкой CreateFile с флагом FILE_FLAG_DELETE_ON_CLOSE и без FILE_SHARE_DELETE

Автор: dma 1.3.2008, 00:54
ух ты! Спасибо! То что доктор прописал smile

Вкратце, получилось так (поправьте, если ошибся):

Код

var
  hsF: THandleStream;
  hwndF: HWND;
begin
  repeat
    Sleep(10);
    hwndF := INVALID_HANDLE_VALUE;
    if FileExists(FileName) then
      hwndF := CreateFile(pchar(FileName), GENERIC_READ, 0, nil,
       OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0)
    else
      break;
  until hwndF <> INVALID_HANDLE_VALUE;
  if hwndF <> INVALID_HANDLE_VALUE then
    hsF := THandleStream.Create(HWNDF);

Автор: bems 1.3.2008, 15:04
1)убери sleep(10)
2)дескриптор окна и объекта ядра это разные вещи, поэтому вместо hwndF: HWND сделай hFile:THandle, хотя формально это не ошибка
3)FileExists убери. Вместо него в условие until добавь проверку, что если CreateFile вернула INVALID_HANDLE_VALUE, то все равно можно закончить цикл если GetLastError вернула ERROR_FILE_NOT_FOUND или ERROR_PATH_NOT_FOUND. Так и от break избавишся
4)добавь секцию try-finally-end, чтобы убедиться что файл будет закрыт, даже если после его открытия возникло исключение
5) ну и если файл существует но занят, то долбиться в него нужно не бесконечно, а до какого-то предела - вдруг тот кто занял подвис, это же не значит, что вслед за ним должен подвиснуть и ты за компанию.

Автор: dma 1.3.2008, 23:45
Снова спасибо за подсказки! smile

Есть пара вопросов:

если Sleep убрать, то этот цикл забьёт весь процесор... а если их ещё и несколько будет, дык вообще вёсла..

в try finally естественно всё это поместиться, я здесь просто рабочий кусок рассматривал smile

вобщем вышло что-то вроде этого:

Код

var
  hsF: THandleStream;
  hF: THandle;
begin
  repeat
    hF := CreateFile(PChar(FileName), GENERIC_READ, 0, nil,
     OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
  until ((Self.Terminated) or (hF <> INVALID_HANDLE_VALUE) or 
    ((GetLastError = ERROR_FILE_NOT_FOUND) or (GetLastError = ERROR_PATH_NOT_FOUND)));
  if hF <> INVALID_HANDLE_VALUE then
    hsF := THandleStream.Create(HF);


кстати, а корректно ли вызывать GetLastError два раза в проверке условия, или же лучше в цикле один раз вызвать и запомнить значение в переменную?

Автор: dma 2.3.2008, 13:38
По поводу GetLastErrorCode почитал мануал, сам себе отвечу:  вроде как правильнее будет сохранить в переменную сразу после вызова CreateFile.
Цитата

You should call the GetLastError function immediately when a function's return value indicates that such a call will return useful data. That is because some functions call SetLastError(0) when they succeed, wiping out the error code set by the most recently failed function. 



Автор: bems 2.3.2008, 15:16
Цитата(dma @  1.3.2008,  23:45 Найти цитируемый пост)
если Sleep убрать, то этот цикл забьёт весь процесор... а если их ещё и несколько будет, дык вообще вёсла..
да, это так. Но со слипом, появляются неоправданые переключения контекста, что тоже плохо. Тут бы впасть в спячку до освобождения.
Файл занять чужим кодом или твоим?

Добавлено через 2 минуты и 45 секунд
как вариант сделать значительно более долгий слип - пару секунд

Автор: dma 2.3.2008, 16:06
Цитата(bems @  2.3.2008,  15:16 Найти цитируемый пост)
Файл занять чужим кодом или твоим?

 И чужим и моим. Когда поток ждёт, пока файл скопируется - он занят чужим кодом, потом, кода один из потоков захватывает файл, остальные ждут - то моим...

Автор: bems 2.3.2008, 16:51
ну тогда проще длинный слип :(

Автор: Riply 3.3.2008, 12:13
Цитата(dma @  29.2.2008,  21:54 Найти цитируемый пост)
Есть так же несколько потоков - обработчиков файлов из этой директории. Вообще говоря каждый поток должен брать и обрабатывать файл своей маски


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

P.S.
 Имеется ввиду именно работа, а не ожидание чего-то.

Автор: Rennigth 3.3.2008, 12:45
Цитата(Riply @  3.3.2008,  12:13 Найти цитируемый пост)
Мне казалось, что использование потоков для работы с файлами, 
расположенными на одном  диске, мякго говоря, - искуственное создание "тормозов".   
Я ошибаюсь ? 

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

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