Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2013.11.17;
Скачать: [xml.tar.bz2];

Вниз

Сохранение файла в 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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.57 MB
Время: 0.009 c
15-1367290964
mk26
2013-04-30 07:02
2013.11.17
Win 8


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


15-1370160003
Ихорь
2013-06-02 12:00
2013.11.17
А вот мог AGP-слот "подгореть"?


2-1360419165
jjda
2013-02-09 18:12
2013.11.17
Блокировка записей Access многопользовательский режим


15-1369773002
Юрий
2013-05-29 00:30
2013.11.17
С днем рождения ! 29 мая 2013 среда





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский