Как я могу читать blobfield без замораживания?
Я хочу прочитать blobfield (с blobstream) со стороны клиента (по сети), но приложение зависает при получении данных. Как я могу читать blobfield без зависания и показа процентов с помощью индикатора выполнения. (Я использую Delphi и Firebird)
Я использую уникальный компонент. я нашел этот код с: http://forums.devart.com/viewtopic.php?t=14629
но это не работает должным образом:
const
BlockSize= $F000;
var
Blob: TBlob;
Buffer: array of byte;
p: pointer;
pos, count: integer;
UniQuery1.SQL.Text:= 'select * from TABLE1 where FIELD_ID = 1';
UniQuery1.Open;
blob:= uniquery1.GetBlob('DATA');
SetLength(buffer, blob.Size);
ProgressBar1.Position:= 0;
Application.ProcessMessages;
repeat
count:= Blob.Read(pos, blocksize, p);
ProgressBar1.Position:= Round(pos/Blob.Size * 100);
pos:= pos + count;
p:= pointer(integer(p) + count);
Application.ProcessMessages;
until count < blocksize;
PS: я установил опции уникальности:
cacheblobs:= false;
streamedblobls:= true;
deferredblobread:= true;
На первом шаге цикла повторения пока метод Blob.Read считывает весь поток, поэтому он не работает должным образом.
1 ответ
Вы должны использовать поток, вот пример с Delphi TThread
:
type
TMyForm = class(TForm)
private
FPosition: Integer;
procedure ProgressUpdate;
procedure Execute;
end;
procedure TMyForm.ProgressUpdate;
begin
ProgressBar1.Position := FPosition;
end;
procedure TMyForm.Execute;
begin
FPosition:= 0;
ProgressUpdate;
Thread := TThread.CreateAnonymousThread(procedure
begin
repeat
// Do some long running stuff (in chunks, so we can update the position)
FPosition := CalculatePosition;
// Important: Synchronize will run ProgressUpdate in the main thread!
TThread.Synchronize(nil, ProgressUpdate);
until SomeCondition;
end
);
Thread.Start;
end;
Поэтому после применения этого шаблона к вашему коду мы получим:
type
TMyForm = class(TForm)
private
FPosition: Integer;
procedure ProgressUpdate;
procedure Execute;
end;
procedure TMyForm.ProgressUpdate;
begin
ProgressBar1.Position := FPosition;
end;
procedure TMyForm.Execute;
var
Blob: TBlob;
Thread: TThread;
begin
UniQuery1.SQL.Text := 'SELECT * FROM TABLE1 WHERE FIELD_ID = 1';
UniQuery1.Open;
Blob := UniQuery1.GetBlob('DATA');
FPosition:= 0;
ProgressUpdate;
Thread := TThread.CreateAnonymousThread(
procedure
const
BlockSize = $F000;
var
Buffer: array of Byte;
P: Pointer;
Pos, Count: Integer;
begin
SetLength(Buffer, Blob.Size);
repeat
Count := Blob.Read(Pos, BlockSize, P);
FPosition := Round(Pos / Blob.Size * 100);
Pos := Pos + Count;
P := Pointer(Integer(P) + Count);
// Important: Synchronize will run ProgressUpdate in the main thread!
TThread.Synchronize(nil, ProgressUpdate);
until Count < BlockSize;
end
);
Thread.Start;
end;
Я удалил Application.ProcessMessage
и перенес всю обработку в поток.
Тема настраивает FPosition
частный атрибут и использует TThread.Synchronize
установить позицию ProgressBar в FPosition
в основной теме.
Если размер вашего блока недостаточно велик, это может блокировать пользовательский интерфейс (из-за чрезмерной синхронизации), поэтому выберите подходящий размер блока или добавьте некоторую задержку обновления.
Вы должны убедиться, что соединение UniQuery1
объект не используется в главном потоке, пока работает анонимный поток, или перемещает соединение, а также запрашивает поток.
Также могут возникнуть проблемы с повторным входом, но это должно дать вам базовое представление о том, как использовать поток для фоновой обработки.
PS: также может быть хорошей идеей запустить запрос в потоке, особенно если это может занять некоторое время.