Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

CreateThread Paramater Problem ?

Posted on 2006-07-08
5
Medium Priority
?
592 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 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

Independent Software Vendors: 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!

Question has a verified solution.

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

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 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…
In this video, Percona Director of Solution Engineering Jon Tobin discusses the function and features of Percona Server for MongoDB. How Percona can help Percona can help you determine if Percona Server for MongoDB is the right solution for …
Is your data getting by on basic protection measures? In today’s climate of debilitating malware and ransomware—like WannaCry—that may not be enough. You need to establish more than basics, like a recovery plan that protects both data and endpoints.…
Suggested Courses

650 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