?
Solved

CreateThread Paramater Problem ?

Posted on 2006-07-08
5
Medium Priority
?
600 Views
Last Modified: 2010-04-05
I have the problem with this code. Problem is that when i compile my program, it crash. Can you test this code and tell me how to fix it. In memoList there is about 5 lines.


procedure Send(pMail : string); Stdcall;
begin
   FormMailSender.Memo1.Lines.Add(pMail);
end;

procedure TFormMailSender.Button1Click(Sender: TObject);
var count : integer;
    Mail : string;
begin
   count := 0;
   while count < MemoList.Lines.Count do
   begin
      Mail := MemoList.Lines.Strings[count];
      lThread := CreateThread(NIL, 0, @Send, Pointer(Mail), 0, lThread);
      count := count + 1;
   end;

end;
0
Comment
Question by:65zgtre45rr
  • 2
  • 2
5 Comments
 
LVL 26

Expert Comment

by:Russell Libby
ID: 17066650
The problem is that you are attempting to update the VCL (by way of Memo1) from secondary threads. The VCL forms / controls / etc run on the main thread, and were not designed to be thread safe, so you will need to synchronize access. The example below demonstrates a simple thread class that is able to safely update the main thread, and performs the same logic as the code you have above.

Regards,
Russell

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TFormMailSender   = class(TForm)
     Button1:       TButton;
     MemoList:      TMemo;
     Memo1:         TMemo;
     procedure      Button1Click(Sender: TObject);
  private
     // Private declarations
  public
     // Public declarations
  end;

type
  TThreadSender     =  class(TThread)
  private
     // Private declarations
     FForm:         TFormMailSender;
     FMail:         String;
  protected
     // Protected declarations
     procedure      Send;
     procedure      Execute; override;
  public
     // Public declarations
     constructor    Create(Form: TFormMailSender; Mail: String);
  end;

var
  FormMailSender:   TFormMailSender;

implementation
{$R *.DFM}


procedure TThreadSender.Send;
begin

  // Call in the main thread
  FForm.Memo1.Lines.Add(FMail);

end;

procedure TThreadSender.Execute;
begin

  // Update the memo in the context of the main thread
  Synchronize(Send);

end;

constructor TThreadSender.Create(Form: TFormMailSender; Mail: String);
begin

  // Save parameters
  FForm:=Form;
  FMail:=Mail;

  // Perform inherited (don't suspend)
  inherited Create(False);

  // Set thread props
  FreeOnTerminate:=True;
  Priority:=tpLower;

end;

procedure TFormMailSender.Button1Click(Sender: TObject);
var  count:      Integer;
     Mail:       String;
begin

  Count:=0;
  while (Count < MemoList.Lines.Count) do
  begin
     Mail:=MemoList.Lines.Strings[Count];
     TThreadSender.Create(Self, Mail);
     Inc(Count);
  end;

end;

end.
0
 

Author Comment

by:65zgtre45rr
ID: 17067573
I think that this is not the main problem, i add this only for example, original code is here. Can you fix this. Thank you !



procedure SendMail(pMail : string); Stdcall;
var
   RequestMailSend, ResponseMailSend: TStringList;
begin
   ResponseMailSend := TStringList.Create;
   RequestMailSend := TStringList.Create;
   FormMailSender.IdHTTP1 := TIdHTTP.Create(nil);

   RequestMailSend.Values['email'] := pMail;
   RequestMailSend.Values['html'] := FormMailSender.MemoHtml.Text;
   RequestMailSend.Values['subject'] := FormMailSender.EditSubject.Text;
   RequestMailSend.Values['from'] := FormMailSender.EditFrom.Text;
   RequestMailSend.Values['fromname'] := FormMailSender.EditFromName.Text;

   ResponseMailSend.Text := FormMailSender.IdHTTP1.Post(FormMailSender.EditSmtpRelayUrl.Text, RequestMailSend);
   FormMailSender.Memo1.Text := ResponseMailSend.Text;
end;

procedure TFormMailSender.Button1Click(Sender: TObject);
var count : integer;
    Mail : string;
begin
   count := 0;
   while count < MemoMailList.Lines.Count do
   begin
      Mail := MemoMailList.Lines.Strings[count];
      lThread := CreateThread(NIL, 0, @SendMail, Pointer(Mail), 0, lThread);
      count := count + 1;
   end;

end;
0
 
LVL 3

Expert Comment

by:wuff
ID: 17072841
Hi, try this one... Regards/Ove
----------------------

// 1. Global variable
// 2. Using global variable twice in SendMail()
// 3. Creating global variable once in Button1Click()
// 4. Initializing global variable to nil when application starts
// 5. Freeing global variable when applicatino ends

implementation

uses SysUtils;

var MySynch : TMultiReadExclusiveWriteSynchronizer; // Global var used for multithreading

procedure SendMail(pMail : string); Stdcall;
var
   RequestMailSend, ResponseMailSend: TStringList;
begin
   MyLock.BeginWrite; // Get write exclusive usage
   try
     ResponseMailSend := TStringList.Create;
     RequestMailSend := TStringList.Create;
     FormMailSender.IdHTTP1 := TIdHTTP.Create(nil);

     RequestMailSend.Values['email'] := pMail;
     RequestMailSend.Values['html'] := FormMailSender.MemoHtml.Text;
     RequestMailSend.Values['subject'] := FormMailSender.EditSubject.Text;
     RequestMailSend.Values['from'] := FormMailSender.EditFrom.Text;
     RequestMailSend.Values['fromname'] := FormMailSender.EditFromName.Text;

     ResponseMailSend.Text := FormMailSender.IdHTTP1.Post(FormMailSender.EditSmtpRelayUrl.Text, RequestMailSend);
     FormMailSender.Memo1.Text := ResponseMailSend.Text;
   finally
      MyLock.EndWrite;  // release write excl access !!!
   end;
end;

procedure TFormMailSender.Button1Click(Sender: TObject);
var count : integer;
    Mail : string;
begin
   if not Assigned(MyLock) then
     MyLock := TMultiReadExclusiveWriteSynchronizer.Create;
   count := 0;
   while count < MemoMailList.Lines.Count do
   begin
      Mail := MemoMailList.Lines.Strings[count];
      lThread := CreateThread(NIL, 0, @SendMail, Pointer(Mail), 0, lThread);
      count := count + 1;
   end;

end;

initialization
  MyLock := nil;
finalization
  if Assigned(MyLock) then
    MyLock.Free;

end.
0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 2000 total points
ID: 17073594

Still sticking by my statement that you should not be accessing the form (in main thread) from the secondary worker threads. I should also mention the multiple errors that exist, such as:

- Creating a component in secondary thread and assigning to the form in the main thread
- Accessing this component (IDHTTP) from the secondary threads. (huge mistake)
- Creating new instances of the component without ever releasing the original instance.
- Memory leaks from creating string lists and never freeing them

You can use the TMultiReadExclusiveWriteSynchronizer example, but I believe the whole intent was to thread the posting process (send mutiple mails at the same time to speed the process). The TMultiReadExclusiveWriteSynchronizer example addresses this by locking the whole sendmail procedure, in effect, making it single threaded.

The following example (based on my original code), demonstrates the before and after sync process, and threading of the posting (each thread has its own idhttp component).

Regards,
Russell

---

type
  TFormMailSender   = class(TForm)
    ...
  end;

type
  TThreadSender     =  class(TThread)
  private
     // Private declarations
     FForm:         TFormMailSender;
     FMailSend:     TStringList;
     FIDHTTP:       TIdHTTP;
     FMailRecv:     String;
     FRelayURL:     String;
     FMail:         String;
  protected
     // Protected declarations
     procedure      AfterSend;
     procedure      BeforeSend;
     procedure      Execute; override;
  public
     // Public declarations
     constructor    Create(Form: TFormMailSender; Mail: String);
  end;

var
  FormMailSender:   TFormMailSender;

implementation
{$R *.DFM}

procedure TThreadSender.BeforeSend;
begin

  // Get relay url
  FRelayURL:=FForm.EditSmtpRelayUrl.Text;

  // Get post parameters
  FMailSend.Values['email']:=FMail;
  FMailSend.Values['html']:=FForm.MemoHtml.Text;
  FMailSend.Values['subject']:=FForm.EditSubject.Text;
  FMailSend.Values['from']:=FForm.EditFrom.Text;
  FMailSend.Values['fromname']:=FForm.EditFromName.Text;

end;

procedure TThreadSender.AfterSend;
begin

  // Call in the main thread
  FForm.Memo1.Text:=FMailRecv;

end;

procedure TThreadSender.Execute;
begin

  // Create http component for mailing
  FIDHTTP:=TIdHTTP.Create(nil);

  // Resource protection
  try
     // Create string list for sending
     FMailSend:=TStringList.Create;
     // Resource protection
     try
        // Get post parameters
        Synchronize(BeforeSend);
        // Perform the post
        FMailRecv:=FIDHTTP.Post(FRelayURL, FMailSend);
        // Set results
        Synchronize(AfterSend);
     finally
        // Free string list
        FMailSend.Free;
     end;
  finally
     // Free component
     FIDHTTP.Free;
  end;

end;

constructor TThreadSender.Create(Form: TFormMailSender; Mail: String);
begin

  // Set parameters
  FForm:=Form;
  FMail:=Mail;
  SetLength(FRelayURL, 0);

  // Perform inherited (don't suspend)
  inherited Create(False);

  // Set thread props
  FreeOnTerminate:=True;
  Priority:=tpLower;

end;

procedure TFormMailSender.Button1Click(Sender: TObject);
var  count:      Integer;
     Mail:       String;
begin

  Count:=0;
  while (Count < MemoList.Lines.Count) do
  begin
     Mail:=MemoList.Lines.Strings[Count];
     TThreadSender.Create(Self, Mail);
     Inc(Count);
  end;

end;



0
 

Author Comment

by:65zgtre45rr
ID: 17074550
Thank you it works
0

Featured Post

Prep for the ITIL® Foundation Certification Exam

December’s Course of the Month is now available! Enroll to learn ITIL® Foundation best practices for delivering IT services effectively and efficiently.

Question has a verified solution.

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

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
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…
Are you ready to place your question in front of subject-matter experts for more timely responses? With the release of Priority Question, Premium Members, Team Accounts and Qualified Experts can now identify the emergent level of their issue, signal…
When cloud platforms entered the scene, users and companies jumped on board to take advantage of the many benefits, like the ability to work and connect with company information from various locations. What many didn't foresee was the increased risk…
Suggested Courses
Course of the Month15 days, 4 hours left to enroll

839 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