Solved

CreateThread Paramater Problem ?

Posted on 2006-07-08
5
583 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
[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
  • 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 500 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

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
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…
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized …
Monitoring a network: why having a policy is the best policy? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the enormous benefits of having a policy-based approach when monitoring medium and large networks. Software utilized in this v…

719 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