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
Solved

CreateThread Paramater Problem ?

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

Networking for the Cloud Era

Join Microsoft and Riverbed for a discussion and demonstration of enhancements to SteelConnect:
-One-click orchestration and cloud connectivity in Azure environments
-Tight integration of SD-WAN and WAN optimization capabilities
-Scalability and resiliency equal to a data center

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Intraweb download file link ? 1 155
TEMBEDDEDWB how can i change its user agent ? 8 69
RESTRequest Parameter 4 42
Delphi: barcode reading on android platform 1 51
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…

856 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