Вопрос

I have some codes here:

procedure TForm1.Button1Click(Sender: TObject);
begin
  //Some Codes(1)
  Sample;
  //Some Codes(2)
end;

Function Sample();
begin
  sleep(5000);
end;

In this code, after //Somecodes(1) Application goes to Sample function and waits for 5 seconds then it runs //Somecodes(2) right? It means for Unfreezing Button1 we have to wait for more than 5 Seconds.

Now I want to do something that when the application runs //Some Codes(1) and Sample, Immediately goes to the next line (//Somecodes(2)) so I don't need to wait for 5 seconds to button 1 Unfreez.

How can I do it?

Это было полезно?

Решение

Like Andreas said, you can use threads for this. You might use them with an anonymous procedure like this: (Note: TThread.CreateAnonymousThread appears not to exist yet in Delphi 2010. I address this problem below.)

procedure TForm1.Button1Click(Sender: TObject);
begin
  TThread.CreateAnonymousThread(
    procedure
    begin
      // Chunk of code 1
      Sleep(5000);
      MessageBox(0, 'x', 'x', MB_OK);
    end).Start;

  TThread.CreateAnonymousThread(
    procedure
    begin
      // Chunk of code 2
      Sleep(5000);
      MessageBox(0, 'y', 'y', MB_OK);
    end).Start;
end;

You can also implement your own thread class, but using CreateAnonymousThread you can just pass a procedure that is executed in the thread. You can call Start (or Run) to run the thread immediately, and threads created using CreateAnonymousThread free themselves when they are done.

So, both chunks of code will run simultaneously. That means that after 5 seconds you will get two popups right after each other (you can drag aside the topmost to see the other). Also, the button will be responsive directly after it is clicked, while the sleeps are running in the background.

Note though that most of the VCL is not thread safe, so you should be careful not to modify (visual) components in these threads.

about thread safety

Like J... mentioned in the comments, it's not just the VCL that is not thread safe. Many code is not, and also your own code needs to be aware of this. For instance if I modify the code above slightly, I can let both threads count to a billion and increment a shared integer. You'd expect the integer to reach 2 billion by the time the threads are done, but in my test it reaches just over one billion. That is because both threads are updating the same integer at the same time, overwriting the attempt of the other thread.

procedure TForm6.FormCreate(Sender: TObject);
var
  n: Integer;
begin
  n := 0;
  TThread.CreateAnonymousThread(
    procedure
    var i: Integer;
    begin
      for i := 0 to 1000000000 do
        Inc(n);
      MessageBox(0, 'x', 'x', MB_OK);
    end).Start;

  TThread.CreateAnonymousThread(
    procedure
    var i: Integer;
    begin
      for i := 0 to 1000000000 do
        Inc(n);
      MessageBox(0, 'y', 'y', MB_OK);
    end).Start;

  Sleep(10000); // Make sure this is long enough. The number should appear later than 'x' and 'y'.
  ShowMessage(IntToStr(n));
end;

To fix this, you can either synchronize the updating (execute it in the main thread), or use critical sections to lock the updates. In cases where you are actually just updating in integer, you can also use the InterlockedIncrement function. I will not explain these further, because there's plenty of proper documentation and it's beyond the scope of this answer.

Note though, that each of these methods slows down your application by executing the pieces of code after each other instead of simultaneously. You are effectively making the threads wait for each other. Nevertheless, threads are still useful, if you can finetune them so that only small pieces need to be synchronised.

TThread.CreateAnonymousThread in Delphi 2010

TThread.CreateAnonymousThread just creates an instance of TAnonymousThread, a specific thread class that executes a given procedure.

Since TThread.CreateAnonymousThread doesn't exist in Delphi 2010, you can just call it like this:

  TAnonymousThread.Create(
    procedure
    var i: Integer;
    begin
      for i := 0 to 1000000000 do
        Inc(n);
      MessageBox(0, 'x', 'x', MB_OK);
    end).Start;

I don't know if TAnonymousThread itself does exist in Delphi 2010, but if not, you can find it's code below. I hope Embarcadero doesn't mind me sharing it, but it's actually just four simple lines of code.

This class you could just as easy create yourself, but it is declared like below. The constructor takes a single parameter, which is the procedure to execute. It also sets a property that makes the thread free itself when done. The execute method just executes the given procedure.

type
  TAnonymousThread = class(TThread)
  private
    FProc: TProc;
  protected
    procedure Execute; override;
  public
    constructor Create(const AProc: TProc);
  end;

constructor TAnonymousThread.Create(const AProc: TProc);
begin
  inherited Create(True);
  FreeOnTerminate := True;
  FProc := AProc;
end;

procedure TAnonymousThread.Execute;
begin
  FProc();
end;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top