Difference between revisions of "Asynchronous Paradigm"

From Overbyte
Jump to navigation Jump to search
(→‎Blocking vs. Non-Blocking: fixed grammar ~~~~)
 
(24 intermediate revisions by 3 users not shown)
Line 3: Line 3:
 
==Non-Blocking==
 
==Non-Blocking==
 
   
 
   
Think of windows programming. It's event driven. Your application doesn't wait in a loop polling the status of a button until it is pressed, instead it gets notified about a button click, in other words an event is triggered. You simply place code to be executed in the event handler.
+
Think of windows programming. It's event driven. Your application doesn't wait in a loop polling the status of a button until it is pressed, instead it gets notified about a button click; in other words an event is triggered. You simply place code to be executed in the event handler.
When the user hits the button event OnClick is fired and your code is run. Very efficient, this is called event-driven or non-blocking or asynchronous (async).
+
When the user hits the button, the OnClick event is fired and your code is run. Very efficient. This functionality is called event-driven, non-blocking or asynchronous (async).
  
When you call an asynchronous method it will return immediately, regardless whether it has finished or not, an event handler will be called later when the method has finished. This also means that your application is capable to do something else in the meanwhile so the following pseudo code suitable for blocking internet components would NOT work with non-blocking ICS components:
+
When you call an asynchronous method it will return immediately, regardless whether it has finished or not; an event handler will be called later when the method has finished. This also means that your application is capable of doing something else in the meanwhile, so the following pseudo code suitable for blocking internet components will '''NOT''' work with non-blocking ICS components:
  
  Connect
+
  Connect;
  Send(Something)  
+
  Send(Something);
  
Because Connect would return immediately without even making sure it has finished successfully and next line would be executed at once. Thus Send(Something) would the fail because it's trying to send data even though there is no connection established to the server yet.
+
''Connect'' will return immediately without even making sure it has finished successfully and the next line will be executed at once. Consequently, ''Send(Something)'' will fail because it's trying to send data even though there is no connection to the server established yet.
  
  
So you need to design your program a bit different. Just like OnClick, the ICS components provide many events allowing you to control program flow. You have to call a method and from then on you rely on events. So you just call Connect, that's it:
+
So we need to design our program a bit different. Just like the events of TButton, the ICS components provide many events allowing us to control program flow. After the event handler has been assigned we have to call the method, from then on we rely on events. So we just call ''Connect'', and that's it:
  
  procedure MySendData;
+
  '''procedure''' TForm1.MySendData;
  begin  
+
  '''begin   '''
     Connect
+
     WSocket1.OnSessionConnected := WSocket1SessionConnected;
  end;
+
    WSocket1.Connect;
 +
  '''end''';
  
Later when method Connect has finished event OnConnect will fire where you can safely send data.
+
Later, when the ''Connect'' method has finished, the '''OnSessionConnected''' event will fire. From within its event handler we can safely send data.
  
  procedure OnConnect(Error: Word);
+
  '''procedure''' TForm1.WSocket1SessionConnected(Sender: TObject; ErrCode: Word);
  begin
+
  '''begin'''
     if Error = 0 then
+
     WSocket1.Send(Something);
        Send(Something)
+
  '''end''';
  end;
 
  
 
Very easy, isn't it?
 
Very easy, isn't it?
 +
 +
The same principle applies when data is received: You never request to read data, you never wait until data is received completely, but simply assign the appropriate event handler to the ''OnDataAvailable'' event and your program gets notified whenever data is available. 
 +
 +
'''procedure''' TForm1.WSocket1DataAvailable(Sender: TObject; ErrCode: Word);
 +
'''var'''
 +
    S : '''String''';   
 +
'''begin'''
 +
    '''if''' ErrCode = 0 '''then''' '''begin'''
 +
        S := WSocket1.ReceiveStr;
 +
        '''if''' S = <font color="Navy">'Hello'#13#10</font> '''then'''
 +
            SendStr(<font color="Navy">'Hi there'#13#10</font>);
 +
    '''end''';           
 +
'''end''';
 +
 +
Note that ''OnDataAvailable'' will fire again if you do not receive all pending data in one go.
  
 
==Blocking==
 
==Blocking==
  
Blocking or synchronous (sync) methods do NOT return until they have finished, so our first code sample would work:     
+
Blocking or synchronous (sync) methods do '''NOT''' return until they have finished, so our first example would work:     
  
  Connect
+
  Connect;
  Send(Something)    
+
  Send(Something);
  
Connect won't return until the connection to the server has been established or an error occured (a timeout may take a very long time ;-). While the program tries to connect it cannot do anything else, it blocks execution even though method connect may just sit there waiting for a server response. For the user it may even looks completely frozen if you don't use multithreading.
+
''Connect'' won't return until the connection to the server has been established or an error occured. While the program tries to connect it cannot do anything else, it blocks execution even though the ''Connect'' method may just sit there waiting for a server response. Blocking calls may even freeze the GUI unless you use multiple threads.
 +
'''''ICS provides asynchronous as well as synchronous methods and components''''' for most upper protocol implementations. Note that '''''ICS does not freeze your GUI''''' because even in synchronous mode, window messages are being processed.
  
 
==Blocking vs. Non-Blocking==
 
==Blocking vs. Non-Blocking==
Line 44: Line 60:
 
Due to the non-blocking nature of ICS it is very easy to handle hundreds of concurrent connections within a single thread.
 
Due to the non-blocking nature of ICS it is very easy to handle hundreds of concurrent connections within a single thread.
 
   
 
   
I give you a pseudo example:
+
Take the following pseudo code example to illustrate how to do this:
  
  procedure MySendData;
+
  '''procedure''' TForm1.MySendData;
  var
+
  '''var'''
 
     I : Integer;     
 
     I : Integer;     
  begin  
+
  '''begin   '''
     for I := 0 to List.Count -1 do
+
     '''for''' I := 0 '''to''' List.Count -1 '''do'''
 
         TWSocket(List[I]).Connect;
 
         TWSocket(List[I]).Connect;
  end;
+
  '''end''';
 
 
procedure OnConnect(Sender: TObject; Error: Word);
 
begin
 
    if Error = 0 then
 
        TWSocket(Sender).Send(Something) 
 
end;
 
 
 
  
With blocking components multiple concurrent connections are only possible if each connection is executed in its own thread context. But threads must be created, freed, synchronized or managed by so called thread pools, this all doesn't speed up things as you can imagine.
+
'''procedure''' TForm1.WSocket1SessionConnect(Sender: TObject; ErrCode: Word);
 +
'''begin'''
 +
    '''if''' ErrCode = 0 '''then'''
 +
        TWSocket(Sender).Send(Something);
 +
'''end''';
  
==How do I process incoming data ?==
 
  
With non-blocking component, you never request to read data, you never wait until data is received. Your program simply do nothing until some data is received. At that time your program receive the OnDataAvailable event. In the corresponding event handler, you call Receive to get the actual data out of the receive buffer. You can then process it, probably producing some respond you can simply send by calling Send.
+
With blocking components, multiple concurrent connections are only possible if each connection is executed in its own thread context. But threads must be created, freed, synchronized or managed by so called '''thread pools'''. This all adds processing overhead, and does not necessarily help speed up your application's performance, as you can imagine.

Latest revision as of 20:00, 30 November 2007

To Block or not to Block

Non-Blocking

Think of windows programming. It's event driven. Your application doesn't wait in a loop polling the status of a button until it is pressed, instead it gets notified about a button click; in other words an event is triggered. You simply place code to be executed in the event handler. When the user hits the button, the OnClick event is fired and your code is run. Very efficient. This functionality is called event-driven, non-blocking or asynchronous (async).

When you call an asynchronous method it will return immediately, regardless whether it has finished or not; an event handler will be called later when the method has finished. This also means that your application is capable of doing something else in the meanwhile, so the following pseudo code suitable for blocking internet components will NOT work with non-blocking ICS components:

Connect;
Send(Something); 

Connect will return immediately without even making sure it has finished successfully and the next line will be executed at once. Consequently, Send(Something) will fail because it's trying to send data even though there is no connection to the server established yet.


So we need to design our program a bit different. Just like the events of TButton, the ICS components provide many events allowing us to control program flow. After the event handler has been assigned we have to call the method, from then on we rely on events. So we just call Connect, and that's it:

procedure TForm1.MySendData;
begin    
    WSocket1.OnSessionConnected := WSocket1SessionConnected;
    WSocket1.Connect;
end;

Later, when the Connect method has finished, the OnSessionConnected event will fire. From within its event handler we can safely send data.

procedure TForm1.WSocket1SessionConnected(Sender: TObject; ErrCode: Word);
begin
    WSocket1.Send(Something);
end;

Very easy, isn't it?

The same principle applies when data is received: You never request to read data, you never wait until data is received completely, but simply assign the appropriate event handler to the OnDataAvailable event and your program gets notified whenever data is available.

procedure TForm1.WSocket1DataAvailable(Sender: TObject; ErrCode: Word);
var
    S : String;     
begin
    if ErrCode = 0 then begin
        S := WSocket1.ReceiveStr;
        if S = 'Hello'#13#10 then
            SendStr('Hi there'#13#10);
    end;             
end;

Note that OnDataAvailable will fire again if you do not receive all pending data in one go.

Blocking

Blocking or synchronous (sync) methods do NOT return until they have finished, so our first example would work:

Connect;
Send(Something);

Connect won't return until the connection to the server has been established or an error occured. While the program tries to connect it cannot do anything else, it blocks execution even though the Connect method may just sit there waiting for a server response. Blocking calls may even freeze the GUI unless you use multiple threads. ICS provides asynchronous as well as synchronous methods and components for most upper protocol implementations. Note that ICS does not freeze your GUI because even in synchronous mode, window messages are being processed.

Blocking vs. Non-Blocking

Due to the non-blocking nature of ICS it is very easy to handle hundreds of concurrent connections within a single thread.

Take the following pseudo code example to illustrate how to do this:

procedure TForm1.MySendData;
var
    I : Integer;    
begin    
    for I := 0 to List.Count -1 do
        TWSocket(List[I]).Connect;
end;
procedure TForm1.WSocket1SessionConnect(Sender: TObject; ErrCode: Word);
begin
    if ErrCode = 0 then
        TWSocket(Sender).Send(Something);
end; 


With blocking components, multiple concurrent connections are only possible if each connection is executed in its own thread context. But threads must be created, freed, synchronized or managed by so called thread pools. This all adds processing overhead, and does not necessarily help speed up your application's performance, as you can imagine.