简体   繁体   中英

How to prevent application from freezing in a long running process?

The app seems to freeze sometimes when we try to pass a lot of imported files at once, which is done a each call of the function below for each file so the proposed solution is to add a sleep, but I can't seem to find proper documentation or explaining on how to handle it, or if I can even pass it as a parameter in a function.

This is the call for the proc

OpenQuery(FOrderToImportQuery.Database,FOrderToImportQuery);

My suggested idea if I can pass Sleep as Param

OpenQuery(FOrderToImportQuery.Database,FOrderToImportQuery, Sleep(200));

This is the function itself minus the sleep

procedure OpenQuery(aDatabase : TIBDatabase; aQuery : TIBQuery);
begin
  if aDatabase.Connected = false  then
    databaseConnect(aDatabase);

  if aDatabase.connected then
  begin
    try
      aQuery.Open;
    except
      //try
        aDatabase.ForceClose;
        aDatabase.Open;
        aQuery.Open;
      {
      except
        on e: exception do
        begin
          Log('Error opening query : '+e.Message);
        end;
      end;
      }
    end;
  end;

end;

The idea is I want the call to wait so it can complete properly before being called again. Would it be just fine to put Sleep at the end of the function itself?(Before the last END)

Or would passing it as a parameter in the call of the function be best? And if is so, how is this achieved... I can't find any doc on this particular circumstance.

The idea is I want the call to wait so it can complete properly before being called again.

Then the idea of using Sleep() is completely misconceived.

If, in a single thread, you call procedures A, B and C, as in

A;
B;
C;

then execution in the thread will only ever proceed to B after the call to A returns. Adding a Sleep() in either of them or in between them will only delay things: if there is a "log-jam" in A , adding a call to Sleep() in or after it will make no difference whatsoever. The fact that A , B and C all call your OpenQuery makes no difference either.

This is true even if A runs an asynchronous query, because the whole point of a call to an asynchronous query is that the call returns before the query completes - an asynchronous query spawns its own background thread in which the query actually executes, then typically passes the results back to the VCL thread via a call to Synchronize() .

You have had comments suggesting that you put your query in a separate worker thread (separate from the VCL thread, that is). That's fine for stopping the VCL thread seizing up while waiting for the query(s) to complete, but including calls to Sleep() in the worker thread won't help there either.

So, the real answer to your q is for you to investigate and solve why a single call to OpenQuery causes the program to hang. But that's not what you've asked ...

First of all, let me say that I'm assuming your code is as optimized as it can be, and the time it takes to complete is inherently long. If you believe this might not be the case, you should open a new question with the details of your queries so we can help you on this.

Sleeping your main thread is definitely not the answer

The Sleep function will actually suspend the main thread for the amount of milliseconds specified. So, you will actually just be freezing your gui even more than now.

Worker thread

Creating a worker thread to handle the long-running work is probably your best bet to keep your program responsive while it's doing all the dirty work.

You'll have to take some precautions, though, because you probably don't want the user to be using the program while it's running the worker thread. For example, you don't want the user to click the start button again; or close the application, etc. But if these precautions are something like freezing the main thread, then you better just freeze it with the long-running work, anyway.

Maybe you will want a cancel button somewhere, if this is a process that can be interrupted in the middle (proper control of database transactions could provide this option safely).

Your worker thread could be something along these lines:

type
  TWorkerThread = class(TThread)
  private
    { Private declarations }
    FDatabase: TIBDatabase;
    FListQueries: TStringList;
  protected
    procedure Execute; override;
  public
    constructor Create(aDatabase: TIBDatabase; ListQueries: TStringList; CreateSuspended: Boolean);
    destructor Destroy; override;
  end;

implementation

{ TWorkerThread }

constructor TWorkerThread.Create(aDatabase: TIBDatabase; ListQueries: TStringList; CreateSuspended: Boolean);
begin
  FListQueries.Create;
  FListQueries.Assign(ListQueries);
  FDatabase := aDatabase;

  inherited Create(CreateSuspended);
end;

destructor TWorkerThread.Destroy;
begin
  FListQueries.Free;
  inherited;
end;

procedure TWorkerThread.Execute;
var i: Integer;
  ibQuery: TIBQuery;
begin
  { Place thread code here }
  ibQuery := TIBQuery.Create(aDatabase);
  try
    for i := 0 to FListQueries.Count - 1 do begin
      if Terminated then
        Exit;

      ibQuery.SQL.Clear;
      ibQuery.SQL.Add(FListQueries[i]);

      OpenQuery(FDatabase, ibQuery);
    end;
  finally
    ibQuery.Free;
  end;
end;

PS: I'm sorry if there are compilation errors or if code for TIBDatabase/TIBQuery is wrong, I don't use any of these.

PPS: There is probably a problem with this code, though: I believe that the TIBConnection is very likely to not be thread-safe (I believe the client library itself is not). So you actually should create one connection just for use within the worker thread, rather than just use the same from main thread. I'll leave this correction for you, though. ;)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM