-2

I have 10 threads working together. After starting the threads, 15 seconds later all threads exit before the job done, and only one thread remains.

My code:

procedure TForm1.Button2Click(Sender: TObject);
begin
  AA;
  BB;
  CC;
  DD;
  EE;
  FF;
  GG;
  HH;
  II;
  JJ;
end;

procedure TForm1.AA;    //same procedure for BB,CC,DD,EE.FF,JJ,HH,II,JJ    
begin
  lHTTP := TIdHTTP.Create(nil);
  TTask.Create(Procedure

    try
      //HTTP Opertations 
    finally
    end;

  end).Start;
end;

Note, i can't Free the HTTP component because if i did i get an AV and I don't know how to debug it, where to correctly free it in the code? However without freeing it the code works well but the threads exit. It might be the problem as Mr Dodge said.

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
Thunderx
  • 169
  • 1
  • 9
  • So, what are you asking, exactly? – MartynA Jul 23 '16 at 21:36
  • 1
    When the thread code is done the thread is destroyed. If you want your thread to live forever you'll need to run an endless loop inside of it. – Johan Jul 23 '16 at 21:39
  • but the thread code is still not finished and the thread exists @Johan – Thunderx Jul 23 '16 at 21:40
  • Besides that though, you haven't provided nearly enough code for us to know what you're doing wrong. What's the actual code performed where you say "HTTP operations"? Perhaps the problem is somewhere in there? – Jerry Dodge Jul 23 '16 at 21:51
  • Thread Exit, i debugged the code @JerryDodge – Thunderx Jul 23 '16 at 21:52
  • Are you performing operations *inside* of the tread, using the `lHTTP` object you created from *outside* the thread? You should never, ever, ever, ever do such a thing. Create that component *inside* the thread, in the same place where it's being used. I also hope you're destroying this component once you're finished using it, otherwise you'll end up with some massive memory leaks. That could also potentially be the culprit of your issue. – Jerry Dodge Jul 23 '16 at 21:52
  • HTTP is created inside every `TForm1.XX` procedures, you mean Freeing the HTTP ? @JerryDodge – Thunderx Jul 23 '16 at 21:59
  • Yes, where do you free the component? Because if you're also freeing it outside of the thread, then there's your problem. – Jerry Dodge Jul 23 '16 at 22:00
  • why i get downvotes ! if any additional information needed i'll provide it – Thunderx Jul 23 '16 at 22:00
  • after `Finally` in every procedure @JerryDodge – Thunderx Jul 23 '16 at 22:01
  • That's precisely your problem then. You should be creating AND freeing it inside the thread where it's being used. Because with your current code (which we cannot actually see), it will start running each thread, but then immediately destroy this component before the thread has done its work. – Jerry Dodge Jul 23 '16 at 22:02
  • how inside the thread? i am using Tasks threads, you mean in `TForm1.Button2Click` or ? thanks @JerryDodge – Thunderx Jul 23 '16 at 22:07
  • Please take a look at the note in the post @JerryDodge – Thunderx Jul 23 '16 at 22:28
  • As you've accepted my answer, if I may ask, exactly which part fixed it? – Jerry Dodge Jul 23 '16 at 23:49
  • PS: Related, but not an actual answer to your question: http://stackoverflow.com/a/36002504/988445 – Jerry Dodge Jul 24 '16 at 01:04

1 Answers1

3

Based on how I see you're creating the TIdHTTP component, it's simply wrong. You shouldn't create an object outside of the thread, then use it from inside the thread. That's not thread-safe. You should create it in the same thread as where it's being used. This is why you're unable to free it as well, so you actually have two problems to fix here at the same time.

I also realized that your lHTTP variable is not in the scope of your code, so I'm going to assume that you have it declared in some global (or otherwise shared) location. Each thread needs its own variable for its own instance.

So your code should look a little more like this:

procedure TForm1.AA;    //same procedure for BB,CC,DD,EE.FF,JJ,HH,II,JJ    
begin
  TTask.Create(Procedure
    var
      lHTTP: TIdHTTP;
    begin
      lHTTP := TIdHTTP.Create(nil);
      try
        //HTTP Opertations 
      finally
        lHTTP.Free;
      end;
    end).Start;
end;

Other components (such as TADOConnection) would even completely fail and crash for attempting such a thing (since such components utilize COM). Luckily, TIdHTTP does not use COM, but the design is still flawed for the same reason.


Now, when you say that you debugged it, I'm guessing you mean you debugged the code in the actual thread, but the breakpoint jumped to another place in your code before it reached the end of this? That is to be expected when using the debugger in threads. You can't just step into a thread and expect each sequential breakpoint to be in the same thread - I mean, if you have more than one breakpoint in different threads, your debugger is very likely to jump from one to another - because, again, they are multiple threads. I suggest creating some sort of work log, and each thread reports its status and position.

It is literally just like an alternate universe. Multiple different similar threads doing slightly different things than each other. The Delphi Debugger is simply the Time Lord who can see into all the alternate universes.

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
  • 1
    I think also the variable `lHTTP`is better to be defined within the thread section. `TTask.Create(Procedure var lHTTP: TIdHTTP; begin ...`, Please correct me if i'm wrong. – RepeatUntil Jul 23 '16 at 23:54
  • @RepeatUntil Thanks for pointing that out, added to my answer. – Jerry Dodge Jul 24 '16 at 00:40
  • 1
    When debugging threads you can use the `Thread Status` window in the IDE to selectively freeze and thaw other running threads. By freezing all other threads you fix the problem outlined above (breakpoints jumping between threads). – J... Jul 24 '16 at 09:29
  • Creating objects in one thread and using in another is not wrong per-se. There are many scenarios when that is reasonable. It all depends...... – David Heffernan Aug 23 '16 at 09:43