Thread containing connection components to the database generates an exception when they are released from memory

Asked

Viewed 2,519 times

3

I needed to add some threads to carry out certain processes in the background.
This thread accesses the methods of a Webservice and also performs operations in the database.

I create all the objects I need to use in this thread within itself to avoid concurrent access with components outside it. Like IBDatabase and the IBTransaction (use IBX components).

So my thread’s kind of like this:

TThreadAtualizarDados = class(TThread)
private
  FIBDatabase: TIBDatabase;
  FIBTransaction: TIBTransaction;      
public
  constructor Create(AIBDatabase: TIBDatabase); reintroduce;
  destructor Destroy; override;
  procedure Execute; override;    
end;

Like I said, everything I need to use in the thread I instill in myself.

constructor TThreadAtualizarDados.Create(AIBDatabase: TIBDatabase);
begin
  inherited Create(false);
  FreeOnTerminate := true;

  // conexão com o banco de dados
  FIBDatabase := TIBDatabase.Create(nil);
  FIBDatabase.LoginPrompt := false;
  FIBDatabase.DatabaseName := AIBDatabase.DatabaseName;
  FIBDatabase.Params.Text := AIBDatabase.Params.Text;

  // controle transacional
  FIBTransaction := TIBTransaction.Create(nil);
  FIBTransaction.DefaultDatabase := FIBDatabase;

  FIBDatabase.DefaultTransaction := FIBTransaction;
end;

The processes I wish her to carry out are successfully carried out until her execution is complete.

destructor TThreadAtualizarDados.Destroy;
begin
  inherited;
  if FIBTransaction.Active then
    FIBTransaction.Commit;

  // Comentados porque está causando erro
  //
  // FIBDatabase.Free;  
  // FIBTransaction.Free;
end;

The lines that end the connection and the transaction are commented because if they are executed, a certain time later (short time) after the thread is finished (method output Destroy) an error is generated.

Error message:

Programa.exe faulted with message: 'application-defined Exception (code 0xc000041d) at 0x77071a91'.

What does not characterize a Access Violation.

What might be causing this error, and what to do to resolve it?


Adding more information:

The thread is instilled this way:

procedure TFrmAtualizaDados.p_Iniciar_Atualizacao;
begin
  TThreadAtualizarDados.Create(Dm.IdbIntsys);
end;

That is, no external treatment is done in the thread. I’m not passing her reference to any variable so that I could be finishing it manually, or anything else. Since I have set her parameter to execute as soon as she is instituted inherited Create(false); and had her destroy herself automatically after finalizing her method Execute, with the FreeOnTerminate := true;, then I just instacing the parameters and let it work.

Even, I have even other classes in the thread that I instâncio, as TStringList and TList that do not give problems, only the instances of the components of the database generate problems if they are released from memory (.Free).


Another detail is that the error message is triggered only when I change the application to Delphi, in mode debug.

Of course the option "Stop on Delphi Exceptions", in "Debuger Options", is enabled.

  • Can you post the code that invokes the threads? See that setting FreeOnTerminate := true; the destroy will already be automatically invoked at the end of the method Execute. That is, if you are invoking the free thread, the code of the destroy will execute twice.

  • 2

    The design you made there is pretty cool. A pity it doesn’t work. Worse is the lack of documentation on these components! I think you’re experiencing yet another sad limitation of Delphi :-/ Maybe these components just can’t work on a second thread, maybe instances share some state that isn’t thread safe, maybe they just don’t work if they’re not pasted into a form or datamodule. All these limitations I have experienced in several Delphi components. Good luck there!

  • 2

    Tiago, a possible outline solution would be to create the connection externally in a pool and just allocate it to the thread. Another test I would do would be to release the transaction first, set FIBDatabase.DefaultTransaction := nil, then set the FIBDatabase.Connected := False and then destroy the object FIBDatabase. Can you tell me if any of these tests were helpful?

  • 2

    Try calling the destroy of objects with Synchronize() to destroy in the main thread. It may be some problem with the Debugger

  • 2

    I will then answer the/

  • 1

    Thiago, I don’t know if TThread implements something in your destructor that can affect your code. In general, it is better to invoke inherited only after your own code on destroy.

  • 1

    I agree with @Caffé, inherited should be the last call on the destructor, including Ctrl+shift+C even leaves room for it, follow the IDE’s suggestion

  • Thiago, in Destroy the "inherited" has to be the last instruction.

Show 3 more comments

2 answers

2

As reported by Tiago Silva, if instantiated, IB**** objects within the scope of the Execute method do not occur. The logical explanation for this is that the components need to be created and destroyed within it Thread. As the method Create runs in Main Thread (Main Thread), and most likely threads are with FreeOnTerminate := True and thus the destructor is executed in the Thread created by the Tthread object, then the problem occurred.

2


Sometimes objects they inherit from TComponent has a strange behavior if destroyed within Threads.

To solve this type of problem, create a method to destroy the objects:

procedure TThreadAtualizaDados.DestruirObjetos;
begin
  FIBDatabase.Free;  
  FIBTransaction.Free;
end;

And in the Destroy of thread destroy them with Synchronize()

destructor TThreadAtualizarDados.Destroy;
begin
  inherited;
  if FIBTransaction.Active then
    FIBTransaction.Commit;

  Synchronize(DestruirObjetos);
end;
  • cc: @Tiagosilva I came back here to see if there was a solution and I’m glad you did. Sad is this: "comportamento estranho", always so present in Delphi, always demanding solutions that at least at first glance work but that leave this code scar not explained in our system. Apparently this is one of those I commented: "maybe these components just can’t work on a second thread".

  • 2

    @Caffé The logical explanation here is that the components must be destroyed in the same thread in which they were created. As they were created in Main Thread, they must be destroyed in Main Thread. To prove, simply instantiate the components in the context of Execute and remove Synchronize

  • 1

    @Caffé If not this, some other factor must be related to "Thread Safety" of these components, "strange behavior" is a term used to describe something we do not know.

  • 2

    @Pagenotfound I hadn’t thought from this angle that the thread create occurs in a different thread than the execution. Great observation and makes perfect sense. Moreover, it makes perfect sense the life cycle of those objects being within the Execute

  • @Caffé About the destructor, it will run in the thread created by Tthread if you have Freeonterminate := True. About the strange behavior, what I meant is lack of technical knowledge to find out if it is really a component bug, or simply a programming error.

  • @Pagenotfound Thiago could test and tell us (I don’t have Delphi and I’m not sure that the destructor is invoked within the second thread or after it’s finished). Possibility of being some factor related to "Thread Safety" of these components was exactly what I said. The definition you passed for "strange behavior" is pretty obvious, I don’t think anyone would think otherwise. The point is to accept strange behavior "solved" by code that we can’t explain why. There was a time I accepted it in my systems. It has been many years since I accepted more.

  • 1

    @Caffé When something happens that I can’t explain, I always think it’s something I’m doing wrong, after all, there were/are excellent software architects working for Borland/Code Gear and now Embarcadero. I think it’s easier for me to make a mistake than they are. Sometimes we break some rules of VCL, Thread, or others and we don’t even notice

  • 1

    @Nice information about the destructor’s behavior. The definition of strange behavior remains obvious and is no different from what I said: "we can’t explain why" :-) And so far no one has suggested it was a bug in the component.

  • @Pagenotfound Nice your behavior before problems in your software. This is undoubtedly the correct!

Show 4 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.