Monday, July 12, 2010

Using Delegates for async method invocation

Problem

I want to call a method asynchronously in a synchronous environment. ie the subsequent lines should execute immediately after the method call regardless how much time the method takes to execute.

Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Debug.WriteLine("Before LongProcess")
TimeConsumingMethod()
Debug.WriteLine("After LongProcess")
End Sub
Private Sub TimeConsumingMethod()
'Imitating time consuming process using Thread.Sleep
System.Threading.Thread.Sleep(2000)
Debug.WriteLine("Done!!!")
End Sub



When we execute the above code it will result as follows.


Before LongProcess

Done!!!

After LongProcess


But what I want is

Before LongProcess

After LongProcess

Done!!!


The immediate answer

If we ask this question to any developer the immediate answer will be “use threading”.Yes that is correct we can use threading.But is there an other easier solution?


Solution

I was in the category of saying “threading” until I heard about the async method invocation using delegates.Here goes the solution.



  • Create a delegate to match with your long running method signature.

  • Declare a delegate variable and instantiate it by passing the address of the long running method.

  • Call the BeginInvoke Method of the delegate which calls the pointed method in asynchronous fashion.



Public Delegate Sub MyAsyncDelegate()
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Debug.WriteLine("Before LongProcess")

Dim del As MyAsyncDelegate = New MyAsyncDelegate(AddressOf TimeConsumingMethod)
del.BeginInvoke(Nothing, Nothing)

Debug.WriteLine("After LongProcess")
End Sub
Private Sub TimeConsumingMethod()
'Imitating time consuming process using Thread.Sleep
System.Threading.Thread.Sleep(20000)
Debug.WriteLine("Done!!!")
End Sub


Passing parameter

You can pass type safe parameters into this methods.For that just change the delegate signature.This automatically ask you to change the function which you are passing into delegate.



Public Delegate Function MyParamAsyncDelegate(ByVal data As String)

Private Function TimeConsumingMethod(ByVal data As String)
'Imitating time consuming process using Thread.Sleep
System.Threading.Thread.Sleep(20000)
Debug.WriteLine("Done!!! Parameter =" & data)
End Function

Private Sub btnCallParam_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Debug.WriteLine("Before LongProcess with param")

Dim del As MyParamAsyncDelegate = New MyParamAsyncDelegate(AddressOf TimeConsumingMethod)
del.BeginInvoke(tbData.Text, Nothing, Nothing)

Debug.WriteLine("After LongProcess with param")
End Sub



Processing return values in async method invocation

When we execute a method we might be expecting some values back after the processing.Now lets see how to get the return value.First change the signature of the method such that it returns something.Obviously return the value.

Now comes a new delegate named AsyncCallback which points to the async delegate execution completed method.We need to pass this delegate variable to the InvokeAsync method of our delegate.

The completed method should accept one parameter of type IAsyncResult which is must when we point using AsyncCallback.The importance of this variable comes when we need the return value.

First we need to cast to AsyncResult which is the concrete implementation of IAsyncResult.Then get the AsyncDelegate property which is our delegate and call the EndInvoke method on that.Confused??The change is only in the completed method where we process the return value.See the below code to get clarified.



Public Delegate Function MyParamAsyncDelegate(ByVal data As String) As Integer

Private Function TimeConsumingMethod(ByVal data As String) As Integer
'Imitating time consuming process using Thread.Sleep
System.Threading.Thread.Sleep(5000)
Debug.WriteLine("Done!!! Parameter =" & data)

'Return the length to read by completed method
Return data.Length
End Function

Private Sub btnCallBackParam_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Debug.WriteLine("Before LongProcess")
Dim delCallBack As MyParamAsyncDelegate = New MyParamAsyncDelegate(AddressOf TimeConsumingMethod)

Dim cb As AsyncCallback = New AsyncCallback(AddressOf TimeConsumingMethodCompleted)
Dim res As IAsyncResult = delCallBack.BeginInvoke(tbDataCallBack.Text, cb, Nothing)

Debug.WriteLine("After LongProcess")
End Sub

Private Sub TimeConsumingMethodCompleted(ByVal ar As System.IAsyncResult)
Dim asr As AsyncResult = DirectCast(ar, AsyncResult)
Dim del As MyParamAsyncDelegate = DirectCast(asr.AsyncDelegate, MyParamAsyncDelegate)
Dim result As Integer = del.EndInvoke(ar)
Debug.WriteLine("Async operation completed method.length of the string :" & result.ToString)
End Sub



Uploaded a sample here

Sunday, July 4, 2010

For Loop in .Bat file

echo off
for %%v in (one two three) do (
echo %%v
)




This is simple usage of for loop in batch file.This can be extended to file reading and more.