0

I'm a novice in Multithreading, i have a procedure that goes through a series of operations using indy (login to a site and download files and ...), sometimes when a site is not responsive or it takes too long, no exception occurs even with idHttp.connectTimeout and idHttp.readTimeout set to a specific amount or sometimes it does occur but not at the time i have specified !!, which is why i prefer to check the task and see if for example an ITask is running for 30 sec, it must be terminated !, i tried using itask.cancel but it only cancels if something is in queue and doesn't terminate a task that is already running, what is the solution to my problem ?

just so you know what is happening in my program :

originally my program creates classes based on a list of profiles and starts a process of login and download, i want to terminate any startDownloadProcess that takes more than 30 sec
here is a code example :

// this is what i used to do
for I := 0 to mProfileList.count - 1 do
begin
  myClass := TMyClass.create(mProfileList[i]);
  //sometimes this takes a very very long time and i don't want that.
  myClass.startDownloadProcess;
end;

// here is what i have in mind 
for I := 0 to mProfileList.count - 1 do
begin
  mITaskArray[i] := TTask.run(procedure
    begin
     myClass := TMyClass.create(mProfileList[i]);
     myClass.startDownloadProcess;
    end);
end;

What i need :
i want each task to depend on the previous one (a queue) and wait until the previous one completes, and if a task takes more than 30 second, it terminates and the queue continues.

now i know there might be a lot of things wrong with that code but that is why i'm here, i'm a bit lost about how to proceed or if what i'm doing is correct at all !

Ali Ahmadi
  • 93
  • 1
  • 3
  • 12
  • 2
    You must regularly check inside the task if it is being cancelled: `if TTask.CurrentTask.Status = TTaskStatus.Canceled then exit;` – LU RD Oct 22 '16 at 10:05
  • @LURD but doesn't the task run only one time ?, if so how can i check for status if it gets stuck at a line (for example IdHttp.get()); – Ali Ahmadi Oct 22 '16 at 11:22
  • 1
    You can't terminate tasks and threads from the outside. You need them to cooperate. You can terminate processes. – David Heffernan Oct 22 '16 at 12:19
  • @DavidHeffernan considering my problem and what i need to do, what do you suggest ? – Ali Ahmadi Oct 22 '16 at 12:31
  • 1
    Exactly what has already been suggesyed – David Heffernan Oct 22 '16 at 12:33
  • Ali, `IdHttp` does its work in a thread of its own. A safe way to stop that work is to define an `OnWork` handler and call `Disconnect` inside that handler if time is out. – LU RD Oct 22 '16 at 15:10
  • 1
    @LURD `TTask`, not `TIdHTTP`, runs in its own thread. `TIdHTTP` does its work in whatever thread is calling `Get`/`Post`. But it doesn't matter, since the `OnWork` events are fired only when bytes are actively being exchanged, so if the site is not responding then there is no socket activity to fire the events. You have to use socket timeouts. Definitely use `ConnectTimeout` and `ReadTimeout` (there is no `SendTimeout` in Indy, but the underlying socket has its own `SO_SNDTIMEO` option at the API level), and also consider enabling TCP keepalives as well. – Remy Lebeau Oct 22 '16 at 15:49
  • @RemyLebeau finally remy, i was waiting for u!, what do you think about an stopwatch that starts at onWorkBegin and resets at onWork, and in another thread it checks and if the stopwatch time passes the threshold, i cause an error so an exception occurs at idhttp, do you think something like this can work ? – Ali Ahmadi Oct 22 '16 at 21:22
  • @AliAhmadi: The only way to "cause an error so an exception occurs" would be to close the `TIdHTTP` socket directly. But even then, that is not guaranteed to work on all platforms. Before resorting to that, I would suggesting trying with socket-level timeouts+keepalives first. – Remy Lebeau Oct 22 '16 at 22:18

1 Answers1

1

you say you are new in multithreading, so remember to never (never) use the TTASK/ITask or similar TParalel. it's completely buggy ! use instead TAnonymousThread

  MyThread := TThread.createAnonymousThread(
    Procedure
    Begin
       repeat
         if MyThread.checkterminated then exit;
         if moreThan30secondsRunning then exit;
         ....
       until wordDone;
    end).start;
zeus
  • 12,173
  • 9
  • 63
  • 184