Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2013.11.17;
Скачать: CL | DM;

Вниз

Сохранение файла в Thread?   Найти похожие ветки 

 
Alex_C   (2011-09-29 12:22) [0]

Посоветуйте как правильно сделать:
есть локальная БД VolgaTable.
После каждого изменения в базе, фаил базы я записываю на диск.
Есть проблема: если в качестве диска использутся флешка, то сохранение происходит порядка 3 сек - что долго. Хочется этот процесс от пользователя "скрыть". Как идея - записывать на диск базе в отдельном Thread.
Вопрос: можно ли это делать? И если да, то как лучше это сделать?


 
stas ©   (2011-09-29 12:26) [1]

Делаете Thread и выполняете в нем процедуру сохранения ).
Так же может понадобится CriticalSection, но это зависит от организации работы с Thread.
Вопрос общий, будут проблемы задавайте более конкретные вопросы...


 
stas ©   (2011-09-29 12:28) [2]

Только я не понял, а база что не на диске хранится?
вообще не правильно перезаписывать всю БД при каждом изменении.


 
Cobalt ©   (2011-09-29 13:00) [3]

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


 
QAZ   (2011-09-29 14:01) [4]


> сохранение происходит порядка 3 сек - что долго. Хочется
> этот процесс от пользователя "скрыть". Как идея - записывать
> на диск базе в отдельном Thread

а что будет,если пользователь не видя процесса сохранения решит выдернуть флешку?


 
Alex_C   (2011-09-29 15:50) [5]

Спасибо за ответы. Поясню - я использую локальную БД  VolgaTable - достаточно старенькая разработка 2000 года, но на мой взгляд ничего лучше для локальных БД так и не придумали. Жаль что автор прекратил ее поддержку.
Работает этот компонент так: база целиком считывается в память, а на диск база сохраняется принудительно через SaveToFile.
С критическими секциями все понятно, у меня такой вопрос - нет ли каких ограничений на запись на диск при работе с Thread?


 
Омлет ©   (2011-09-29 15:56) [6]

> нет ли каких ограничений на запись на диск при работе с Thread

Всё те же, что и в основном потоке.

> достаточно старенькая разработка 2000 года, но на мой взгляд ничего лучше для локальных БД так и не придумали

SQLite.


 
Alex_C   (2011-09-29 17:13) [7]

Немного не в тему: есть ли для SQLite надстройка, позволяющая использовать SQLite как и BDE (чтоб мого кода не перелапачивать)?


 
Ega23 ©   (2011-09-29 17:21) [8]


>  у меня такой вопрос - нет ли каких ограничений на запись
> на диск при работе с Thread?


ИМХО, дополнительный tread тут ваще не в тему. Не ускоришь.


 
sniknik ©   (2011-09-29 17:53) [9]

> ваще не в тему. Не ускоришь.
только запутаешь... типа нажал на сохранить, оно мигнуло и сказало Ок, а сомо в это время в потоке пыжится, записывает... но пользователь уже видит Ок!!! и что ему мешает флешку выдернуть?
вот авто сохранение, типа "бекапа изменений в ворде" другое дело...

> После каждого изменения в базе, фаил базы я записываю на диск.
?
> но на мой взгляд ничего лучше для локальных БД так и не придумали.
брать таблицу в памяти, и вместо изменений сохранять всю? где логика?


 
Cobalt ©   (2011-09-29 18:20) [10]

Частые перезаписи убивают флешку, как бы.
Так что в этом всё верно.
Представь, что вместо базы у тебя какой-нить txt или xml
На каждый чих записывать?


 
QAZ   (2011-09-29 19:22) [11]


> я использую локальную БД  VolgaTable - достаточно старенькая
> разработка 2000 года, но на мой взгляд ничего лучше для
> локальных БД так и не придумали. Жаль что автор прекратил
> ее поддержку.

вообще ее последняя версия от 2006 года


 
Ega23 ©   (2011-09-29 21:25) [12]


> вообще ее последняя версия от 2006 года


вообще с 2006 года 5 лет прошло... :)


 
Slym ©   (2011-09-30 06:48) [13]

искать в компоненте SaveToStream и делать типа
db.SaveToStream(MemoryStream);//быстрая операция в памяти
ThreadSaveStream(MemoryStream);//в потоке не блокирую интерфейс сохраняем Stream

база не блокируется во время фонового сохранения


 
Alex_C   (2011-10-01 09:37) [14]


> искать в компоненте SaveToStream и делать типа


Прикольно - именно так и сделал в итоге :)
А тут смотрю - и совет такой же. Значит верное направление :)


 
sniknik ©   (2011-10-01 11:20) [15]

> Значит верное направление :)
два идиота нашли друг друга... совет вам да любовь.

на критику в нелогичности самой идеи внимания лучше не обращать... ну и что что для изменений единственной позиции делается перезапись всей много мегабайтной (иначе откуда тормоза в 3 сек?) таблицы? и хрен с ним что гробится флешка и т.д. там где любой нормальный движок изменит только саму запись (4кб если она в одном кластере диска) и займет это менее тысячной доли секунды на любом размере файла, без всяких извращений...
идиот уже решил, что выбранный им движок/логика работы самая верная, и просит он не советов, а подтверждений своему решению... на все остальное прикрыть глаза иначе не дай бог придется учить новое, и переделывать свое творение по каким то странным для него правилам работы из учебников... свой "велосипед" он же ближе к телу, роднее.


 
Inovet ©   (2011-10-01 13:12) [16]

> [6] Омлет ©   (29.09.11 15:56)
> для локальных БД так и не придумали
>
> SQLite.

Firebird embedded.

Атор таблицк называет базой?


 
Inovet ©   (2011-10-01 13:13) [17]

> [9] sniknik ©   (29.09.11 17:53)
> брать таблицу в памяти, и вместо изменений сохранять всю?
> где логика?

Флешку жалко


 
Dennis I. Komarov ©   (2011-10-06 22:36) [18]


> Поясню - я использую локальную БД  VolgaTable - достаточно
> старенькая разработка 2000 года, но на мой взгляд ничего
> лучше для локальных БД так и не придумали.


> Немного не в тему: есть ли для SQLite надстройка, позволяющая
> использовать SQLite как и BDE (чтоб мого кода не перелапачивать)?
>

Достаточно что бы отправить в печку...


 
jack128_   (2011-10-06 22:39) [19]


> ThreadSaveStream(MemoryStream);//в потоке не блокирую интерфейс
> сохраняем Stream

зачем здесь поток нужен? 21 век, откройте для себя асинхронный ввод/вывод.


 
Dennis I. Komarov ©   (2011-10-06 22:44) [20]


> зачем здесь поток нужен? 21 век, откройте для себя асинхронный
> ввод/вывод.

Не в этой серии...


 
Alex_C   (2011-10-07 15:58) [21]


> откройте для себя асинхронный ввод/вывод.


Большое спасибо за конструктивный совет! Сделано!
Для тех, кому интересно, очень хорошая статья по этому поводу тут:
http://pblog.ru/?p=74


 
Alex_C   (2011-10-14 09:34) [22]

В продолжении темы: сделал асинхронное сохранение:


type
 TSaveFile = class(TThread)
   FFileName:    string;
 protected
   procedure Execute; override;
 public
   constructor Create(FileName: string);
 end;

var
 SaveStream: TMemoryStream;
 SaveTableCS: TRTLCriticalSection;

constructor TSaveFile.Create(FileName: string);
begin
 FreeOnTerminate := True;
 FFileName := FileName;
 inherited Create(False);
end;

procedure TSaveFile.Execute;
var
 FileHandle: THandle;
 Overlap: TOverlapped;
 res: Boolean;
 WriteBytes, LastError: DWORD;
 WriteArray: array of byte;
begin
 EnterCriticalSection(SaveTableCS);
 try
   try
     SetLength(WriteArray, SaveStream.Size);
     SaveStream.Position := 0;
     SaveStream.ReadBuffer(WriteArray[0], SaveStream.Size);
     FillChar(Overlap, SizeOf(Overlap), 0);
     FileHandle := CreateFile(PChar(FFileName), GENERIC_WRITE , 0, Nil, CREATE_ALWAYS,
       FILE_FLAG_OVERLAPPED, 0);
     if FileHandle = INVALID_HANDLE_VALUE then
       Application.MessageBox(PChar("Can not write to file: " + FFileName),
         "Error!", MB_OK)
     else
     begin
       Overlap.hEvent := CreateEvent(Nil, True, False, Nil);
       res := WriteFile(FileHandle, WriteArray[0], Length(WriteArray), WriteBytes, @Overlap);
       LastError := GetLastError;

       if not res then
       begin
         if LastError = ERROR_IO_PENDING then
           while  WaitForSingleObject(Overlap.hEvent, 100) = WAIT_TIMEOUT do
             Application.ProcessMessages
         else
           Application.MessageBox(PChar("Can not write to file: " + FFileName),
             "Error!", MB_OK)
       end;

       GetOverlappedResult(FileHandle, Overlap, WriteBytes, False);
       CloseHandle(Overlap.hEvent);
       CloseHandle(FileHandle);
     end;
   except
     Application.MessageBox(PChar("Can not create file " + FFileName), "Error", MB_OK);
   end;
 finally
   SetLength(WriteArray, 0);
   LeaveCriticalSection(SaveTableCS);
 end;
end;

procedure SaveTableToFile(Table:TVolgaTable; FileName: string);
begin
 SaveStream.Clear;
 Table.SaveToStream(SaveStream);
 TSaveFile.Create(FileName);
end;


У меня на нескольких компьютерах проверил: теперь что на флешку, что на диск тормозов при записи нет. Однако, у некоторых пользователей они остались. Вопрос: почему такое могло произойти?


 
Slym ©   (2011-10-14 10:18) [23]

вот нафея в ОТДЕЛЬНОМ потоке асинхронный вывод? ты кроме сохранения что нибудь еще в этом потоке делаешь?


 
Alex_C   (2011-10-14 10:21) [24]


> ОТДЕЛЬНОМ потоке асинхронный вывод?


отдельный поток остался от прошлых идей - я его конечно уберу. Теперь он не нужен. Но! Проблема то не в нем...


 
Alex_C   (2011-10-14 10:29) [25]

Может я чего не правильно делаю? Убрал поток. Сделал так:


procedure SaveTableToFile(Table:TVolgaTable; FileName: string);
var
 FileHandle: THandle;
 Overlap: TOverlapped;
 res: Boolean;
 WriteBytes, LastError: DWORD;
 WriteArray: array of byte;
begin
 SaveStream.Clear;
 Table.SaveToStream(SaveStream);
 SetLength(WriteArray, SaveStream.Size);
 SaveStream.Position := 0;
 SaveStream.ReadBuffer(WriteArray[0], SaveStream.Size);
 FillChar(Overlap, SizeOf(Overlap), 0);
 FileHandle := CreateFile(PChar(FileName), GENERIC_WRITE , 0, Nil, CREATE_ALWAYS,
   FILE_FLAG_OVERLAPPED, 0);
 if FileHandle = INVALID_HANDLE_VALUE then
   Application.MessageBox(PChar("Can not write to file: " + FileName),
     "Error!", MB_OK)
 else
 begin
   Overlap.hEvent := CreateEvent(Nil, True, False, Nil);
   res := WriteFile(FileHandle, WriteArray[0], Length(WriteArray), WriteBytes, @Overlap);
   LastError := GetLastError;

   if not res then
   begin
     if LastError = ERROR_IO_PENDING then
       while  WaitForSingleObject(Overlap.hEvent, 100) = WAIT_TIMEOUT do
         Application.ProcessMessages
     else
       Application.MessageBox(PChar("Can not write to file: " + FileName),
         "Error!", MB_OK)
   end;

   GetOverlappedResult(FileHandle, Overlap, WriteBytes, False);
   CloseHandle(Overlap.hEvent);
   CloseHandle(FileHandle);
 end;
 SetLength(WriteArray, 0);
end;


При записи на флешку появились 3-х секундные тормоза.
Может я асинхронную запись неправильно делаю?


 
Alex_C   (2011-10-14 10:33) [26]

Вернул сейчас поток назад - тормоза при записи на флешку пропали...


 
Slym ©   (2011-10-14 13:39) [27]

похоже на запись асинхронка не действует :)
проверил сей код (заменой write на read +мелочи) на чтении - работает


 
Slym ©   (2011-10-14 13:40) [28]

Alex_C   (14.10.11 10:29) [25]
Table.SaveToStream(SaveStream);
SetLength(WriteArray, SaveStream.Size);
SaveStream.Position := 0;
SaveStream.ReadBuffer(WriteArray[0], SaveStream.Size);


ReadFile(FileHandle, MemoryStream.Memory^, MemoryStream.Size,WriteBytes, @Overlap);


 
Alex_C   (2011-10-14 14:58) [29]


> ReadFile(FileHandle, MemoryStream.Memory^, MemoryStream.
> Size,WriteBytes, @Overlap);


Спасибо за подсказку! Действительно отпадает надобность в лишнем действии!

Однако по основному вопросу все равно остается вопрос: почему не действует асинхронная запись?
Сделал 4 варианта работы программы:
1. Вариант - запись на флешку Stream.SaveToFile из осносного потока - 3 сек "зависания" программы.
2. Тоже - из отдельного треда - около 1 сек "зависа".
3. При помощи WriteFileEx из основного потока - 3 сек.
4. Тоже из отдельного - 1 сек.
В итоге получается, что асинхронный вариант "не работает". Вопрос: почему?
Вот итоговый код для пункта 4:

type
TSaveFile = class(TThread)
  FFileName:    string;
protected
  procedure Execute; override;
public
  constructor Create(FileName: string);
end;

var
 SaveStream: TMemoryStream;
 SaveTableCS: TRTLCriticalSection;

constructor TSaveFile.Create(FileName: string);
begin
 FreeOnTerminate := True;
 FFileName := FileName;
 inherited Create(False);
end;

procedure TSaveFile.Execute;
var
 FileHandle: THandle;
 Overlap: TOverlapped;
 WriteBytes, LastError: DWORD;
 WriteArray: array of byte;
begin
 EnterCriticalSection(SaveTableCS);
 try
   SetLength(WriteArray, SaveStream.Size);
   SaveStream.Position := 0;
   SaveStream.ReadBuffer(WriteArray[0], SaveStream.Size);
   FileHandle := CreateFile(PChar(FFileName), GENERIC_WRITE , 0, Nil, CREATE_ALWAYS,
     FILE_FLAG_OVERLAPPED, 0);
   if FileHandle = INVALID_HANDLE_VALUE then
     Application.MessageBox(PChar("Can not write to file: " + FFileName),
       "Error!", MB_OK)
   else
   begin
     FillChar(Overlap, SizeOf(Overlap), 0);
     Overlap.hEvent := CreateEvent(Nil, True, False, Nil);
     //res := WriteFile(FileHandle, WriteArray[0], Length(WriteArray), WriteBytes, @Overlap);
     if not WriteFileEx(FileHandle, @WriteArray[0], Length(WriteArray), Overlap, nil) then
     begin
       LastError := GetLastError;
       if LastError = ERROR_IO_PENDING then
       begin
         if not ((WaitForSingleObject(Overlap.hEvent, 100) = WAIT_OBJECT_0) and
           GetOverlappedResult(FileHandle, Overlap, WriteBytes, False)) then
           Application.MessageBox(PChar("Can not write to file: " + FFileName),
             "Error!", MB_OK)
       end;
     end;
     CloseHandle(Overlap.hEvent);
     CloseHandle(FileHandle);
   end;
 finally
   SetLength(WriteArray, 0);
   LeaveCriticalSection(SaveTableCS);
 end;
end;

procedure SaveTableToFile(Table:TVolgaTable; FileName: string);
begin
 SaveStream.Clear;
 Table.SaveToStream(SaveStream);
 TSaveFile.Create(FileName);
end;

Для пункта 3 - он такой же, но в основном потоке.
Вопрос: что не так?
P.S. Очень бы хотелось получить ответы у авторов постов 19 и 20, которые по видимому что то знают, но не хотят говорить :)


 
Alex_C   (2011-10-14 15:03) [30]

да забыл исправить, в соответствии с [28] будет:


     if not WriteFileEx(FileHandle, @SaveStream.Memory^, SaveStream.Size, Overlap, nil) then



 
jack128_   (2011-10-14 17:07) [31]

Ну так ты же сразу после начала сохранения файла _ждешь_ завершения записи данных.  А должен указать последним параметром WriteFileEx указатель на калбек(который будет вызван после того как все данные запишутся)


 
Alex_C   (2011-10-14 17:41) [32]


> Ну так ты же сразу после начала сохранения файла _ждешь_
> завершения записи данных.  А должен указать последним параметром
> WriteFileEx указатель на калбек(который будет вызван после
> того как все данные запишутся)
>


Спасибо за ответ!
Не катит.  Пробовал.
Поясню. Сначала пробовал просто WriteFile (он кстати закомментирован в примере). Ничего не меняется.
А калбек вызывается только если он ЕСТЬ! Это ведь всего лишь индикатор того, что ф-ция завершила свою работу. Вроде как идеологию тут я правильно понимаю.
Но не суть - и WriteFile дает тот же результат.


 
b z   (2011-10-15 11:19) [33]


> Вроде как идеологию тут я правильно понимаю.
Нет. Калбэк должен вызываться всегда, если его инициализировали. И в нем уже анализировать результат работы.


 
Alex_C   (2011-10-16 13:51) [34]


> если его инициализировали


Так собственно я об этом же и говорю - в выше приведенном примере я его не инициализирую - он и не должен вызываться.

Однако дело не в нем. Как вариант: может ли антивирус давать результат, при котором асинхронная запись не работает?


 
b z   (2011-10-16 15:52) [35]

если не инициализировали то и анализировать нечего


 
jack128_   (2011-10-18 16:57) [36]

Примерно так код должен выглядить:

unit Unit4;

interface

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

type
 TForm4 = class(TForm)
   Button1: TButton;
   ApplicationEvents1: TApplicationEvents;
   procedure ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
   procedure FormCreate(Sender: TObject);
   procedure Button1Click(Sender: TObject);
 public
   { Public declarations }
 end;

var
 Form4: TForm4;

implementation

{$R *.dfm}

type
 PAsyncSaveStreamData = ^TAsyncSaveStreamData;
 TAsyncSaveStreamData = record
   FileHandle: THandle;
   Overlapped: TOverlapped;
   AfterSaveAction: TProc;
 end;

procedure SaveStreamToFileAsyncCallback(
 dwErrorCode: DWORD;
 dwNumberOfBytesTransfered: DWORD;
 lpOverlapped: POverlapped); stdcall;
var
 Data: PAsyncSaveStreamData;
begin
 Data := Pointer(lpOverlapped.hEvent);
 try
   // тут нужно обработать ошибки..
   Data.AfterSaveAction();
 finally
   Win32Check(CloseHandle(Data.FileHandle));
   Dispose(Data);
 end;
end;

function WriteFileEx(hFile: THandle; lpBuffer: Pointer; nNumberOfBytesToWrite: DWORD;
 lpOverlapped: POverlapped; lpCompletionRoutine: FARPROC): BOOL; stdcall; external kernel32 name "WriteFileEx";

procedure SaveStreamToFileAsync(AStream: TMemoryStream; AFilename: string; AfterSaveAction: TProc);
var
 Handle: THandle;
 Data: PAsyncSaveStreamData;
begin
 Handle := CreateFile(PChar(AFilename), GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0);
 Win32Check(Handle <> INVALID_HANDLE_VALUE);
 New(Data);
 try
   ZeroMemory(Data, SizeOf(TAsyncSaveStreamData));
   Data.FileHandle := Handle;
   Data.Overlapped.hEvent := Cardinal(Data);
   Data.AfterSaveAction := AfterSaveAction;
   Win32Check(WriteFileEx(Handle, AStream.Memory, AStream.Size, @Data.Overlapped, @SaveStreamToFileAsyncCallback));
 except
   Win32Check(CloseHandle(Handle));
   Dispose(Data);
   raise;
 end;
end;

procedure TForm4.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
begin
 MsgWaitForMultipleObjectsEx(0, PInteger(nil)^, 0, QS_ALLEVENTS, MWMO_ALERTABLE);
end;

procedure TForm4.FormCreate(Sender: TObject);
begin
 ReportMemoryLeaksOnShutdown := True;
end;

procedure TForm4.Button1Click(Sender: TObject);
var
 Stream: TMemoryStream;
begin
 Stream := TMemoryStream.Create;
 try
   Stream.SetSize(10* 1024 * 1024);
   SaveStreamToFileAsync(Stream, "X:\1.dat", procedure
     begin
       Stream.Free;
       ShowMessage("Ура");
     end);
 except
   Stream.Free;
   raise;
 end;
end;

end.


object Form4: TForm4
 Left = 0
 Top = 0
 Caption = "Form4"
 ClientHeight = 337
 ClientWidth = 635
 Color = clBtnFace
 Font.Charset = DEFAULT_CHARSET
 Font.Color = clWindowText
 Font.Height = -11
 Font.Name = "Tahoma"
 Font.Style = []
 OldCreateOrder = False
 OnCreate = FormCreate
 PixelsPerInch = 96
 TextHeight = 13
 object Button1: TButton
   Left = 280
   Top = 216
   Width = 75
   Height = 25
   Caption = "Button1"
   TabOrder = 0
   OnClick = Button1Click
 end
 object ApplicationEvents1: TApplicationEvents
   OnIdle = ApplicationEvents1Idle
   Left = 312
   Top = 176
 end
end


 
Alex_C   (2011-10-18 17:16) [37]

Большое спасибо!



Страницы: 1 вся ветка

Текущий архив: 2013.11.17;
Скачать: CL | DM;

Наверх




Память: 0.58 MB
Время: 0.006 c
1-1318430295
plr
2011-10-12 18:38
2013.11.17
Не могу разобраться с runtime error


1-1317284571
Alex_C
2011-09-29 12:22
2013.11.17
Сохранение файла в Thread?


15-1360441805
Юрий
2013-02-10 00:30
2013.11.17
С днем рождения ! 10 февраля 2013 воскресенье


15-1370205002
Юрий
2013-06-03 00:30
2013.11.17
С днем рождения ! 3 июня 2013 понедельник


15-1370032202
Юрий
2013-06-01 00:30
2013.11.17
С днем рождения ! 1 июня 2013 суббота