OmniThreadLibrary forum
News: SMF - Just Installed!
 
*
Welcome, Guest. Please login or register. May 17, 2012, 06:07:17 PM


Login with username, password and session length


Pages: [1]   Go Down

Author Topic: Sending messages from an event handler in a thread?  (Read 336 times)

dahook

  • Newbie
  • *
  • Posts: 2
    • View Profile
Sending messages from an event handler in a thread?
« on: January 14, 2012, 04:46:23 PM »

Hi,

I've somewhat feared multithreading due to the learning curve, but your presentations inspired me to take the leap... Thanks a lot for the Omnithread Library, its clear that there is a lot of time and effort behind it!
Being a "weekend programmer", I'm far from mastering the outs and ins of Delphi, so forgive me if my question is simple or dumb.

I'm attaching a small project which queries a web service with SOAP. My question is if it is possible to send messages from a thread from an event that occurs within the thread. In my case, I would like to get messages when events is generated from the HTTPRIO component in order to see progress on the web service request. Since the reference to IOmniTask is not in scope at that time, I can't use task.Comm.Send. Even if it is in scope, I mean if I declare it global, how can I know which thread that cause the event if more than one thread talks to the web service at one time?

Example:
Code: [Select]
    procedure Form1.HTTPRIO1BeforeExecute(const MethodName: string; SOAPRequest: TStream);
    begin
      ??.Comm.Send...
    end;

The attached project's response and request is rather quick, but in my company I communicate a web service where responses sometimes is quite big, like in several MB big. It might take 20 seconds for the response to be transmitted , and I would like to be able to monitor that there is progress.
In my sample I just write to a listbox from the events, but from what I understand I'm actually writing to the listbox from within the thread which I probably should not do.

Can you advice me how I can solve this in a better way? Also, is there anything else in my code that looks odd regarding the Omnithread usage?

Kind Regards,

Dan
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Sending messages from an event handler in a thread?
« Reply #1 on: January 16, 2012, 02:24:36 PM »

Appropriate way to solve such problem is to create a descendant of the TOmniWorker object and move THTTPRIO/THTTPReqResp there. You should also create and destroy them inside the thread, and you should declare all event handlers as methods inside this descendant of the TOmniWorker object. Like this:

Code: [Select]
type
  TMoviesTask = class(TOmniWorker)
  strict private
    FHTTPReqResp: THTTPReqResp;
    FHTTPRIO: THTTPRIO;
  protected
    procedure CreateRIO;
    procedure DestroyRIO;
    procedure HTTPReqRespBeforePost(const HTTPReqResp: THTTPReqResp; Data: Pointer);
    procedure HTTPRIOAfterExecute(const MethodName: string; SOAPResponse: TStream);
    procedure HTTPRIOBeforeExecute(const MethodName: string; SOAPRequest: TStream);
    procedure HTTPReqRespPostingData(Sent, Total: Integer);
    procedure HTTPReqRespReceivingData(Read, Total: Integer);
  public
    procedure GetTop10;
  end;

procedure TMoviesTask.CreateRIO;
begin
  FHTTPReqResp := THTTPReqResp.Create(nil);
  with FHTTPReqResp do begin
    Name := 'HTTPReqRespTask' + IntToStr(Task.UniqueID);
    UseUTF8InHeader := True;
    InvokeOptions := [soIgnoreInvalidCerts, soAutoCheckAccessPointViaUDDI];
    WebNodeOptions := [];
    OnBeforePost := HTTPReqRespBeforePost;
    OnPostingData := HTTPReqRespPostingData;
    OnReceivingData := HTTPReqRespReceivingData;
  end;
  FHTTPRIO := THTTPRIO.Create(nil);
  with FHTTPRIO do begin
    Name := 'HTTPRIOTask' + IntToStr(Task.UniqueID);
    OnAfterExecute := HTTPRIOAfterExecute;
    OnBeforeExecute := HTTPRIOBeforeExecute;
    HTTPWebNode := FHTTPReqResp;
  end;
end;

procedure TMoviesTask.HTTPReqRespBeforePost(const HTTPReqResp: THTTPReqResp; Data: Pointer);
begin
  Task.Comm.Send(SOAP_MSG, Format('CurrThread %d : MainThread %d ', [GetCurrentThreadId(), MainThreadID]) + 'HTTPReqResp1BeforePost');
end;

procedure TMoviesTask.GetTop10;
var
  res: TopMovies.ArrayOfString;
  I: Integer;
  resList: TStringList;
begin
  if CoInitializeEx(nil, COINIT_MULTITHREADED or COINIT_SPEED_OVER_MEMORY) = S_OK then try
    CreateRIO;
    try
      try
        res := GetTopMoviesSoap(False, '', FHTTPRIO).GetTop10;
        resList := TStringList.Create;
        if Length(res) > 0 then
          for I := 0 to Length(res) - 1 do
            resList.Add(res[I]);
        task.Comm.Send(SOAP_DONE, resList);
      except
        on E: Exception do
          task.Comm.Send(SOAP_DONE, E);
      end;
    finally DestroyRIO; end;
  finally CoUninitialize; end;
end;

Code: [Select]
procedure TMainForm.btnGetTop10Omni1Click(Sender: TObject);
begin
  lbLog.Clear;
  lbLog.Items.Add(Format('CurrThread %d : MainThread %d ', [GetCurrentThreadId(), MainThreadID]) + 'btnGetTop10Omni1Click start');
  lbSoapResponse.Clear;
  FGetSoapTask := CreateTask(TMoviesTask.Create(), 'MoviesTask')
    .OnMessage(
{$REGION 'Message handler'}
      procedure(const task: IOmniTaskControl; const msg: TOmniMessage)
      begin
        case msg.MsgID of
          SOAP_MSG:
            lbLog.Items.Add(msg.MsgData);
          SOAP_DONE:
          begin
            lbSoapResponse.Items := TStrings(msg.MsgData.AsObject);
          end;
          SOAP_ERROR:
          begin
            lbLog.Items.Add(Format('CurrThread %d : MainThread %d ', [GetCurrentThreadId(), MainThreadID])
                                 + 'Error: ' + msg.MsgData.AsException.Message);
          end;
        end;
      end)
{$ENDREGION}
    .Run.Invoke(@TMoviesTask.GetTop10);

  lbLog.Items.Add(Format('CurrThread %d : MainThread %d ', [GetCurrentThreadId(), MainThreadID]) + 'btnGetTop10Omni1Click end');
end;

Full object is attached.

I can not, however, destroy THTTPRIO/THTTPReqRequest components in the thread. If I try this, I'm getting back Invalid Pointer exception from the VCL and I can't tell what's going wrong. (This happens in XE2, didn't try other Delphis.)

I think I'll ask on StackOverflow for help with this detail ...
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Sending messages from an event handler in a thread?
« Reply #2 on: January 16, 2012, 04:38:48 PM »

It has turned out that you only have to free THTTPRIO and ignore THTTPReqResp because THTTPRIO will destroy it.

Just keep in FreeAndNil(FHTTPRIO) and comment out FreeAndNil(FHTTPReqResp) and everything will work.
Logged

dahook

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: Sending messages from an event handler in a thread?
« Reply #3 on: January 17, 2012, 04:50:20 AM »

Thanks, that explained a lot to me! I saw that others in the forum had created TOmniWorker descendants for various thing but could not get a grasp on how that would help me, but your reply clearly explains it. Thanks again for a good answer and very nice framework, I'm looking forward to implementing multithreading in my applications with it  :)
Logged
Pages: [1]   Go Up
 
 

Powered by MySQL Powered by PHP Powered by SMF 2.0.2 | SMF © 2006-2009, Simple Machines LLC

Valid XHTML 1.0! Valid CSS! Dilber MC Theme by HarzeM