?
Solved

Thread

Posted on 2003-03-30
17
Medium Priority
?
352 Views
Last Modified: 2010-04-04
I have a thread and I execute it by the help of a timer
which triggers at 1ms.

type
 TMyThread = class(TThread)
   procedure Execute; override;
   constructor Create; overload;
 end;

constructor TMyThread.Create;
begin
     inherited Create(False);
end;
.
.
.
var myt:MyThread;
.
.
.
myt:=MyThread.Create;
myt.Priority:=tpNormal;
.
.
.
myt.execute;
.
.
.
myt.Free;

Sometimes, when the program tries to create the thread
it fails and an error message is received. If I want to
create it again it also fails. But, sometimes it works
without any problem. It behaves like this for any code
in the thread's execute part. Am I doing something wrong
or is this because of something else? Can I correct
this? How? Should I make the thread in a different way?
If so, how can I do that? Please, help me.
0
Comment
Question by:MikeMonroe
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 4
  • 3
  • +3
17 Comments
 
LVL 9

Expert Comment

by:mocarts
ID: 8233452
you don't need to call myt.Free; as thread is freed automatically when terminated or Execute completes..
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 8233612
You do not understand threads at all.
Calling myt.Execute is completely wrong. It is called automatically by MyThread.Create(False) or by a call to myt.Resume.
Using a 1 msec normal timer is also silly. You cannot get less than 10 msec from a normal timer.
Why do you use a timer at all? The thread runs on its own.
0
 

Author Comment

by:MikeMonroe
ID: 8234045
I am using a timer because I want to run the thread multiple times, without blocking the programs execution. It doesn't free automatically(I did it this way).
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 4

Expert Comment

by:Colin_Dawson
ID: 8234228
I've made a couple of adjustments to your code.  Give this a try.  Firstly, I've set the code so that the thread will auto free itself.  Secondly I've introduced a loop that will check to see if you've requested that the thread be terminated.

In order to stop this thread processing, simplly call  MyThread.Terminate It's then save to assume that the thread has been freed.

You should only ever need to create one thread for a task then add more information for that thread to process.



type
TMyThread = class(TThread)
  procedure Execute; override;
  constructor Create; overload;
end;

constructor TMyThread.Create;
begin
  inherited Create(True); //Creates the thread suspended.
  FreeOnTerminate := True;  //Tell the thread to free after completion of the execute method.
  Resume; //Set the thread going
end;

Procedure TMyThread.Execute;
Begin
  While Not Terminated Do
  Begin
    If WorkToDo Then
    Begin
      //Process your thread code here.
    End
    Else Sleep( 1 ); //Sleeps to a millisecond
  End;
End;
0
 

Author Comment

by:MikeMonroe
ID: 8237029
I still get errors like 'Out of system resources' or 'Invalid handle' or 'Access denied'. Maby they occur because the thread can not be created or accessed at certain times(that's why sometimes it works the first time and sometimes not, maybe). If I put the code in a timer it works, so it isn't the fault of the code in the thread, but the thread's fault.
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 8237201
Do you really want to start a thread each msec?
The thread only does a single task and then terminates?
Do you still call Execute? Use Resume!

If you use your above source then it cannot work because it is completely wrong.

If you use a single variable myt to hold the thread object then you will overwrite it on each newly created thread and you orphan any previously started thread.
You always free (and terminate) the last thread created. Since you do not wait for the thread to terminate you cause real problems.
You should set the threads to free themselves. It is no problem then to orphan them because you do not need to free it anymore.

What are the threads doing? Do they compete for resources?
0
 

Author Comment

by:MikeMonroe
ID: 8238025
Show me how you make threads and run them please. I think it's better than correcting my thread, to write a new one. So, I need the thread to run as long as the program runs. It's better if I can stop it and restart it.
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 8238105
type
  TMyThread = class(TThread)
  public
    SomeThreadVariable: Integer;
    procedure Execute; override;
  end;


procedure TYourForm.TimerTimer(Sender: TObject);
begin
  // best avoid a variable
  // we create the thread suspended
  with TMyThread.Create(True) do
  begin
    // since we soon lose track of the thread we let it free itself when finished
    FreeOnTerminate := True;
    // set thread specific variables before the thread runs
    SomeThreadVariable := 1;
    // let the thread run
    Resume;
  end;  
end;

procedure TMyThread.Execute;
begin
  // for safety reasons
  try
    // NO "while not Terminated do" loop here or the thread would never terminate
    DoSomething;
  except
    // catches all exceptions
  end;
end;

I doubt that the above setup will work if you start a thread each msec. If the thread needs longer than a msec to terminate then you will be flooded with threads and each thread will take even longer to terminate.
In the end your program will choke on the threads.

Can you tell what you want to achieve each msec?
I would assume that a better setup would be only to have a single thread which keeps a job list and your main program adds each msec a new job to the list.
0
 

Author Comment

by:MikeMonroe
ID: 8239334
I am scanning for some updates in my thread. That's why I need it to do the same thing(start scan, scan, stop scan, start again...) until the program finishes. So, I used the timer to start the thread as soon as it finished. That's why it was set up at 1 msec. It's like a procedure I want to call again as soon as it finishes. Using a timer or recursion wouldn't provide the best solution for my program, so that's why I need a thread.
0
 
LVL 9

Expert Comment

by:mocarts
ID: 8239476
then write OnTerminate event handler for thread:
TForm1 = class(TForm)
..
  procedure FormCreate(Sender: TObject);
private
  procedure ThreadTerminated(Sender: TObject);
  procedure StartNewThread;
end;

implementation

procedure TForm1.StartNewThread;
begin
    with TMyThread.Create(True) do begin
      // set required properties
     OnTerminate := ThreadTerminated;
     Resume;
    end;
end;

procedure TForm1.ThreadTerminated(Sender: TObject);
begin
  // executes in context of main thread
  if not Application.Terminated {or some other flag} then
    StartNewThread;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  StartNewThread;
end;    

wbr, mo.
0
 
LVL 2

Accepted Solution

by:
steve_hsk earned 180 total points
ID: 8241952
Hi Mike,

I guess it's just a different perspective, but I don't like the idea of continually starting and stopping 1000's of threads to do very small jobs. There are potentially lots of reasons why this is bad not least of all that your giving your app and the OS a massive amount of work thread switching, resource handling, and memory management to do when it is all unnecessary.

The only valid reason behind using threads to ensure that operations can happen supposedly simulataneously. I guess we need to know more about exactly what you're trying achieve. I'll suggest an idea that may be a little of the mark ... but it's better programming practice than to start and stop threads continusouly.

We'll work with two threads, master and slave, where the master will instruct the slave to undertake some work on a timer. After the slave has completed the work, it simply waits to be instructed for the next 'job'.

There are two basic window's technologies we'll use here, threads, and window's messages. The master creates the slave, which waits for a message from the master. On the timer operating within the master, we simple post a msg to the thread !

I use this method when writing extremely fast network decoders, that need to undertake 3000 network msg decodes per second, and I can assure you that it's fast, effective, simple, and not memory or CPU hungry :

// SLAVE CODE ...

UNIT Threadbase;
INTERFACE
USES Windows, Classes;

CONST WM_DO_STUFF_1 = WM_USER + 1001;
CONST WM_DO_STUFF_2 = WM_USER + 1002;

TYPE TThreadBase = CLASS (TThread)
PUBLIC    
     // Class Operating Internals
     CONSTRUCTOR Create;  VIRTUAL;
     DESTRUCTOR  Destroy; VIRTUAL;

     // Member Object Instances
     PROCEDURE Execute; OVERRIDE;
     PROCEDURE DecipherMessage (Msg : TMsg); VIRTUAL;
END;

IMPLEMENTATION

CONSTRUCTOR TThreadBase.Create;
BEGIN
     // Inherit parent properties :Create suspended
     INHERITED Create(TRUE);
     FreeOnTerminate := FALSE;
     Priority        := tpHigher;    
END;

DESTRUCTOR TThreadBase.Destroy;
BEGIN
     INHERITED DESTROY;
END;

PROCEDURE TThreadBase.Execute;
VAR Msg : TMsg;
BEGIN
    WHILE NOT (terminated) DO
    BEGIN
        dError := GetMessage(Msg,0,0,0);

        IF (dError = TRUE) THEN
        BEGIN
            DecipherMessage(Msg);

            WHILE (peekmessage(Msg,0,0,0,PM_REMOVE) = True) DO
            BEGIN
                DecipherMessage(Msg);
            END;
        END
        ELSE IF (dError = FALSE) THEN
        BEGIN
            // has received the WM_QUIT msg
            Terminate;
        END
        ELSE
        BEGIN
            // error has occurred : may wish to handle
            Terminate;
        END
    END;    
END;

PROCEDURE TThreadBase.DecipherMessage (Msg : TMsg);
BEGIN
     CASE Msg.MESSAGE OF

        WM_DO_STUFF_1 :
        BEGIN    
             // Do your operations here
        END;

        WM_DO_STUFF_2 :
        BEGIN    
             // Do your operations here
        END;
END;

// MASTER CODE ...

UNIT Unit1;
INTERFACE
USES Windows, Messages, Classes, Controls, Forms, Dialogs,
     StdCtrls, ExtCtrls, ComCtrls, Threadbase;

TYPE TForm1 = class(TForm)
PUBLISHED
    Button1  : TButton;
    Timer1   : TTimer;
    PROCEDURE FormCreate  (Sender: TObject);
    PROCEDURE Timer1Timer (Sender: TObject);
    PROCEDURE FormDestroy (Sender: TObject);
PRIVATE
    m_tSlave : TThreadBase;
END;

VAR Form1: TForm1;
IMPLEMENTATION
{$R *.DFM}

PROCEDURE TForm1.FormCreate(Sender: TObject);
BEGIN
    m_tSlave := TThreadBase.Create;
    m_tSlave.Resume;
    Timer1.Enabled := FALSE;
END;

PROCEDURE TForm1.Button1Click(Sender: TObject);
BEGIN
    Timer1.Enabled := NOT(Timer1.Enabled);
END;

PROCEDURE TForm1.Timer1Timer(Sender: TObject);
BEGIN
    // Insert any data you want the slave thread to have into Lparam() and Wparam() as below ...
    PostThreadMessage(m_tSlave.ThreadID,
                      WM_DO_STUFF_1,
                      WPARAM(0),  
                      LPARAM(0));
END;

PROCEDURE TForm1.FormDestroy(Sender: TObject);
BEGIN
    // Method 1:
    PostThreadMessage(m_tSlave.ThreadID,
                      WM_QUIT,
                      0,  
                      0);
   
    // Method 2:
    // m_tSlave.Terminate;

    Sleep(1000); // must give the thread a chance to end

    IF m_tSlave <> NULL THEN
    BEGIN
        m_tSlave.Free;
    END;
END;

Note : As robert_marquardt said, the TTimer object, although has msecs unit, only has  a 10 msec resolution. If you need anymore detailled code, or an example for a 1 msec timer, let me know, and I'll post a threaded example up.

So as (I hope) you can see from the code, we're not stopping and starting multiple threads, just one. When the threads got nothing to do, it waits on the GetMessage function call, until a msg arrives to action.

I hope this all makes sense.
STeve
0
 

Author Comment

by:MikeMonroe
ID: 8245011
For my application I just need to capture a certain part of the screen continuously. I know how to capture the screen, but this is a very intensive CPU activity. That's why I want to use a thread. The capture code has no problem. I just need to encapsulate it in a thread which should execute the capture continuously when I tell it to start and until I tell it to finish. I didn't use threads before, so I need an exact soure code sample of a thread that can do what I said. To steve_hsk: Your idea is interesting, but too complex for my program's needs. I think it can be done in a simpler way.
0
 
LVL 2

Expert Comment

by:steve_hsk
ID: 8245171
Mike,

I can appreciate that complexity is simply perspective when looking at a problem. This is of course your project and you must be completely comfortable with the coding methods and technologies that you use. I'll simply say that :-

1) For good programming practice, I think it would be a wise move to learn this fundamental method.
2) The code I listed for you is in full working order. All that you need to do is to insert your screen capture code into the following block :-

PROCEDURE TThreadBase.DecipherMessage (Msg : TMsg);
BEGIN
    CASE Msg.MESSAGE OF
       WM_DO_STUFF_1 :
       BEGIN    
            // Screen Capture code goes here !
       END;
END;

and I missed a variable declaration for dError here :

PROCEDURE TThreadBase.Execute;
VAR Msg : TMsg; dError : Boolean;

And this code should compile and work for you. I thought about making a fast threaded screen capture component for you, as we already have nearly all the code necessary, but the points aren't quite high enough for that ;-)

Good luck in finding a solution you feel happy with,
STeve
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 8245420
A thread will not help you to lower the CPU load. Quite the contrary.

This is a job where a thread is the silliest idea possible.
Capturing the screen in a timer without threads will work better. If the capturing needs more than a msec then a thread cannot help at all.
0
 

Author Comment

by:MikeMonroe
ID: 8245586
I am not using the thread to lower the CPU load, but to allow the program to continue executing while the capture takes place without using ProcessMessages.
0
 
LVL 9

Expert Comment

by:mocarts
ID: 8246610
const
  iInterval = 100; // milliseconds
TMyThread.Execute;
var
 t: dword;
begin
  while not Terminated do begin
    if (GetTickCount - t) >= iInterval then
    begin
      CaptureScreen;
      // or before CaptureScreen, if you need more exact timing
      t := GetTickCount;
    end;
  end;
end;
0
 

Expert Comment

by:CleanupPing
ID: 9316543
MikeMonroe:
This old question needs to be finalized -- accept an answer, split points, or get a refund.  For information on your options, please click here-> http:/help/closing.jsp#1 
EXPERTS:
Post your closing recommendations!  No comment means you don't care.
0

Featured Post

Enroll in August's Course of the Month

August's CompTIA IT Fundamentals course includes 19 hours of basic computer principle modules and prepares you for the certification exam. It's free for Premium Members, Team Accounts, and Qualified Experts!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Have you created a query with information for a calendar? ... and then, abra-cadabra, the calendar is done?! I am going to show you how to make that happen. Visualize your data!  ... really see it To use the code to create a calendar from a q…
In this video, Percona Solution Engineer Dimitri Vanoverbeke discusses why you want to use at least three nodes in a database cluster. To discuss how Percona Consulting can help with your design and architecture needs for your database and infras…
Suggested Courses

764 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question