Solved

CreateThread Paramater Problem ?

Posted on 2006-07-08
5
577 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 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

Gigs: Get Your Project Delivered by an Expert

Select from freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely and get projects done right.

Question has a verified solution.

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

Suggested Solutions

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…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …
Established in 1997, Technology Architects has become one of the most reputable technology solutions companies in the country. TA have been providing businesses with cost effective state-of-the-art solutions and unparalleled service that is designed…

816 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

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now